Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add option to match some of the labes instead of all #2122 #2123

Merged
merged 4 commits into from
Jun 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,7 @@ In case the setup does not work as intended follow the trace of events:
| <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 |
| <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 |
| <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 |
| <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 |
| <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 |
| <a name="input_runner_group_name"></a> [runner\_group\_name](#input\_runner\_group\_name) | Name of the runner group. | `string` | `"Default"` | no |
| <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 |
Expand Down
1 change: 1 addition & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ module "webhook" {

# labels
enable_workflow_job_labels_check = var.runner_enable_workflow_job_labels_check
workflow_job_labels_check_all = var.runner_enable_workflow_job_labels_check_all
runner_labels = "self-hosted,${var.runner_os},${var.runner_architecture},${var.runner_extra_labels}"

role_path = var.role_path
Expand Down
3 changes: 2 additions & 1 deletion modules/setup-iam-permissions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,9 @@ No modules.
|------|-------------|------|---------|:--------:|
| <a name="input_account_id"></a> [account\_id](#input\_account\_id) | The module allows to switch to the created role from the provided account id. | `string` | n/a | yes |
| <a name="input_aws_partition"></a> [aws\_partition](#input\_aws\_partition) | (optional) partition in the arn namespace if not aws | `string` | `"aws"` | no |
| <a name="input_environment"></a> [environment](#input\_environment) | A name that identifies the environment, used as prefix and for tagging. | `string` | n/a | yes |
| <a name="input_environment"></a> [environment](#input\_environment) | A name that identifies the environment, used as prefix and for tagging. | `string` | `null` | no |
| <a name="input_namespaces"></a> [namespaces](#input\_namespaces) | The role will be only allowed to create roles, policies and instance profiles in the given namespace / path. All policies in the boundaries namespace cannot be modified by this role. | <pre>object({<br> boundary_namespace = string<br> role_namespace = string<br> policy_namespace = string<br> instance_profile_namespace = string<br> })</pre> | n/a | yes |
| <a name="input_prefix"></a> [prefix](#input\_prefix) | The prefix used for naming resources | `string` | `"github-actions"` | no |

## Outputs

Expand Down
1 change: 1 addition & 0 deletions modules/webhook/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ No modules.
| <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 |
| <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 |
| <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 |
| <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 |

## Outputs

Expand Down
42 changes: 42 additions & 0 deletions modules/webhook/lambdas/webhook/src/webhook/handler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ describe('handler', () => {
it('Check runner labels accept test job', async () => {
process.env.RUNNER_LABELS = '["self-hosted", "test"]';
process.env.ENABLE_WORKFLOW_JOB_LABELS_CHECK = 'true';
process.env.WORKFLOW_JOB_LABELS_CHECK_ALL = 'true';
const event = JSON.stringify({
...workflowjob_event,
workflow_job: {
Expand All @@ -142,6 +143,7 @@ describe('handler', () => {
it('Check runner labels accept job with mixed order.', async () => {
process.env.RUNNER_LABELS = '["linux", "test", "self-hosted"]';
process.env.ENABLE_WORKFLOW_JOB_LABELS_CHECK = 'true';
process.env.WORKFLOW_JOB_LABELS_CHECK_ALL = 'true';
const event = JSON.stringify({
...workflowjob_event,
workflow_job: {
Expand All @@ -160,6 +162,7 @@ describe('handler', () => {
it('Check webhook does not accept jobs where not all labels are provided in job.', async () => {
process.env.RUNNER_LABELS = '["self-hosted", "test", "test2"]';
process.env.ENABLE_WORKFLOW_JOB_LABELS_CHECK = 'true';
process.env.WORKFLOW_JOB_LABELS_CHECK_ALL = 'true';
const event = JSON.stringify({
...workflowjob_event,
workflow_job: {
Expand All @@ -178,6 +181,7 @@ describe('handler', () => {
it('Check webhook does not accept jobs where not all labels are supported by the runner.', async () => {
process.env.RUNNER_LABELS = '["self-hosted", "x64", "linux", "test"]';
process.env.ENABLE_WORKFLOW_JOB_LABELS_CHECK = 'true';
process.env.WORKFLOW_JOB_LABELS_CHECK_ALL = 'true';
const event = JSON.stringify({
...workflowjob_event,
workflow_job: {
Expand All @@ -192,6 +196,44 @@ describe('handler', () => {
expect(resp.statusCode).toBe(202);
expect(sendActionRequest).not.toBeCalled;
});

it('Check webhook will accept jobs with a single acceptable label.', async () => {
process.env.RUNNER_LABELS = '["self-hosted", "x64", "linux", "test"]';
process.env.ENABLE_WORKFLOW_JOB_LABELS_CHECK = 'true';
process.env.WORKFLOW_JOB_LABELS_CHECK_ALL = 'false';
const event = JSON.stringify({
...workflowjob_event,
workflow_job: {
...workflowjob_event.workflow_job,
labels: ['x64'],
},
});
const resp = await handle(
{ 'X-Hub-Signature': await webhooks.sign(event), 'X-GitHub-Event': 'workflow_job' },
event,
);
expect(resp.statusCode).toBe(201);
expect(sendActionRequest).toBeCalled();
});

it('Check webhook will not accept jobs without correct label when job label check all is false.', async () => {
process.env.RUNNER_LABELS = '["self-hosted", "x64", "linux", "test"]';
process.env.ENABLE_WORKFLOW_JOB_LABELS_CHECK = 'true';
process.env.WORKFLOW_JOB_LABELS_CHECK_ALL = 'false';
const event = JSON.stringify({
...workflowjob_event,
workflow_job: {
...workflowjob_event.workflow_job,
labels: ['ubuntu-latest'],
},
});
const resp = await handle(
{ 'X-Hub-Signature': await webhooks.sign(event), 'X-GitHub-Event': 'workflow_job' },
event,
);
expect(resp.statusCode).toBe(202);
expect(sendActionRequest).not.toBeCalled;
});
});

describe('Test for check_run event (legacy): ', () => {
Expand Down
24 changes: 18 additions & 6 deletions modules/webhook/lambdas/webhook/src/webhook/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ const supportedEvents = ['check_run', 'workflow_job'];
const logger = rootLogger.getChildLogger();

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

// ensure header keys lower case since github headers can contain capitals.
for (const key in headers) {
Expand Down Expand Up @@ -66,6 +67,7 @@ export async function handle(headers: IncomingHttpHeaders, body: string): Promis
payload as WorkflowJobEvent,
githubEvent,
enableWorkflowLabelCheck,
workflowLabelCheckAll,
runnerLabels,
);
} else if (githubEvent == 'check_run') {
Expand All @@ -79,11 +81,13 @@ function readEnvironmentVariables() {
const environment = process.env.ENVIRONMENT;
const enableWorkflowLabelCheckEnv = process.env.ENABLE_WORKFLOW_JOB_LABELS_CHECK || 'false';
const enableWorkflowLabelCheck = JSON.parse(enableWorkflowLabelCheckEnv) as boolean;
const workflowLabelCheckAllEnv = process.env.WORKFLOW_JOB_LABELS_CHECK_ALL || 'false';
const workflowLabelCheckAll = JSON.parse(workflowLabelCheckAllEnv) as boolean;
const repositoryWhiteListEnv = process.env.REPOSITORY_WHITE_LIST || '[]';
const repositoryWhiteList = JSON.parse(repositoryWhiteListEnv) as Array<string>;
const runnerLabelsEnv = process.env.RUNNER_LABELS || '[]';
const runnerLabels = JSON.parse(runnerLabelsEnv) as Array<string>;
return { environment, repositoryWhiteList, enableWorkflowLabelCheck, runnerLabels };
return { environment, repositoryWhiteList, enableWorkflowLabelCheck, workflowLabelCheckAll, runnerLabels };
}

async function verifySignature(
Expand Down Expand Up @@ -117,9 +121,10 @@ async function handleWorkflowJob(
body: WorkflowJobEvent,
githubEvent: string,
enableWorkflowLabelCheck: boolean,
workflowLabelCheckAll: boolean,
runnerLabels: string[],
): Promise<Response> {
if (enableWorkflowLabelCheck && !canRunJob(body, runnerLabels)) {
if (enableWorkflowLabelCheck && !canRunJob(body, runnerLabels, workflowLabelCheckAll)) {
logger.warn(
`Received event contains runner labels '${body.workflow_job.labels}' that are not accepted.`,
LogFields.print(),
Expand Down Expand Up @@ -171,10 +176,17 @@ function isRepoNotAllowed(repoFullName: string, repositoryWhiteList: string[]):
return repositoryWhiteList.length > 0 && !repositoryWhiteList.includes(repoFullName);
}

function canRunJob(job: WorkflowJobEvent, runnerLabels: string[]): boolean {
function canRunJob(job: WorkflowJobEvent, runnerLabels: string[], workflowLabelCheckAll: boolean): boolean {
const workflowJobLabels = job.workflow_job.labels;
const runnerMatch = runnerLabels.every((l) => workflowJobLabels.includes(l));
const jobMatch = workflowJobLabels.every((l) => runnerLabels.includes(l));
let runnerMatch;
let jobMatch;
if (workflowLabelCheckAll) {
runnerMatch = runnerLabels.every((l) => workflowJobLabels.includes(l));
jobMatch = workflowJobLabels.every((l) => runnerLabels.includes(l));
} else {
runnerMatch = runnerLabels.some((l) => workflowJobLabels.includes(l));
npalm marked this conversation as resolved.
Show resolved Hide resolved
jobMatch = workflowJobLabels.some((l) => runnerLabels.includes(l));
}
const match = jobMatch && runnerMatch;

logger.debug(
Expand Down
6 changes: 6 additions & 0 deletions modules/webhook/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,12 @@ variable "enable_workflow_job_labels_check" {
default = false
}

variable "workflow_job_labels_check_all" {
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."
type = bool
default = true
}

variable "log_type" {
description = "Logging format for lambda logging. Valid values are 'json', 'pretty', 'hidden'. "
type = string
Expand Down
1 change: 1 addition & 0 deletions modules/webhook/webhook.tf
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ resource "aws_lambda_function" "webhook" {
environment {
variables = {
ENABLE_WORKFLOW_JOB_LABELS_CHECK = var.enable_workflow_job_labels_check
WORKFLOW_JOB_LABELS_CHECK_ALL = var.workflow_job_labels_check_all
ENVIRONMENT = var.prefix
LOG_LEVEL = var.log_level
LOG_TYPE = var.log_type
Expand Down
6 changes: 6 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,12 @@ variable "runner_enable_workflow_job_labels_check" {
default = false
}

variable "runner_enable_workflow_job_labels_check_all" {
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."
type = bool
default = true
}

variable "runner_ec2_tags" {
description = "Map of tags that will be added to the launch template instance tag specificatons."
type = map(string)
Expand Down