Skip to content

feat: Natively support runner pre/post job hooks #4263

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

Merged
merged 5 commits into from
Dec 20, 2024
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,8 @@ Talk to the forestkeepers in the `runners-channel` on Slack.
| <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_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. | `list(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_hook_job_completed"></a> [runner\_hook\_job\_completed](#input\_runner\_hook\_job\_completed) | Script to be ran in the runner environment at the end of every job | `string` | `""` | no |
| <a name="input_runner_hook_job_started"></a> [runner\_hook\_job\_started](#input\_runner\_hook\_job\_started) | Script to be ran in the runner environment at the beginning of every job | `string` | `""` | 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 |
| <a name="input_runner_log_files"></a> [runner\_log\_files](#input\_runner\_log\_files) | (optional) Replaces the module default cloudwatch log config. See https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Agent-Configuration-File-Details.html for details. | <pre>list(object({<br/> log_group_name = string<br/> prefix_log_group = bool<br/> file_path = string<br/> log_stream_name = string<br/> }))</pre> | `null` | no |
| <a name="input_runner_metadata_options"></a> [runner\_metadata\_options](#input\_runner\_metadata\_options) | Metadata options for the ec2 runner instances. By default, the module uses metadata tags for bootstrapping the runner, only disable `instance_metadata_tags` when using custom scripts for starting the runner. | `map(any)` | <pre>{<br/> "http_endpoint": "enabled",<br/> "http_put_response_hop_limit": 1,<br/> "http_tokens": "required",<br/> "instance_metadata_tags": "enabled"<br/>}</pre> | no |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@ runner_config:
runners_maximum_count: 1
delay_webhook_event: 0
scale_down_schedule_expression: cron(* * * * ? *)
runner_hook_job_started: |
echo "Running pre job hook as \$(whoami)"
runner_hook_job_completed: |
echo "Running post job hook as \$(whoami)"
14 changes: 14 additions & 0 deletions examples/multi-runner/templates/user-data.sh
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,18 @@ ${post_install}

cd /opt/actions-runner

%{ if hook_job_started != "" }
cat > /opt/actions-runner/hook_job_started.sh << EOF
${hook_job_started}
EOF
echo ACTIONS_RUNNER_HOOK_JOB_STARTED=/opt/actions-runner/hook_job_started.sh | tee -a /opt/actions-runner/.env
%{ endif }

%{ if hook_job_completed != "" }
cat > /opt/actions-runner/hook_job_completed.sh << EOF
${hook_job_completed}
EOF
echo ACTIONS_RUNNER_HOOK_JOB_COMPLETED=/opt/actions-runner/hook_job_completed.sh | tee -a /opt/actions-runner/.env
%{ endif }

${start_runner}
2 changes: 2 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ module "runners" {
userdata_content = var.userdata_content
userdata_pre_install = var.userdata_pre_install
userdata_post_install = var.userdata_post_install
runner_hook_job_started = var.runner_hook_job_started
runner_hook_job_completed = var.runner_hook_job_completed
key_name = var.key_name
runner_ec2_tags = var.runner_ec2_tags

Expand Down
2 changes: 1 addition & 1 deletion modules/multi-runner/README.md

Large diffs are not rendered by default.

16 changes: 9 additions & 7 deletions modules/multi-runner/runners.tf
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,15 @@ module "runners" {
role_path = var.role_path
role_permissions_boundary = var.role_permissions_boundary

enable_userdata = each.value.runner_config.enable_userdata
userdata_template = each.value.runner_config.userdata_template
userdata_content = each.value.runner_config.userdata_content
userdata_pre_install = each.value.runner_config.userdata_pre_install
userdata_post_install = each.value.runner_config.userdata_post_install
key_name = var.key_name
runner_ec2_tags = each.value.runner_config.runner_ec2_tags
enable_userdata = each.value.runner_config.enable_userdata
userdata_template = each.value.runner_config.userdata_template
userdata_content = each.value.runner_config.userdata_content
userdata_pre_install = each.value.runner_config.userdata_pre_install
userdata_post_install = each.value.runner_config.userdata_post_install
runner_hook_job_started = each.value.runner_config.runner_hook_job_started
runner_hook_job_completed = each.value.runner_config.runner_hook_job_completed
key_name = var.key_name
runner_ec2_tags = each.value.runner_config.runner_ec2_tags

create_service_linked_role_spot = each.value.runner_config.create_service_linked_role_spot

Expand Down
4 changes: 4 additions & 0 deletions modules/multi-runner/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ variable "multi_runner_config" {
cloudwatch_config = optional(string, null)
userdata_pre_install = optional(string, "")
userdata_post_install = optional(string, "")
runner_hook_job_started = optional(string, "")
runner_hook_job_completed = optional(string, "")
runner_ec2_tags = optional(map(string), {})
runner_iam_role_managed_policy_arns = optional(list(string), [])
vpc_id = optional(string, null)
Expand Down Expand Up @@ -180,6 +182,8 @@ variable "multi_runner_config" {
cloudwatch_config: "(optional) Replaces the module default cloudwatch log config. See https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Agent-Configuration-File-Details.html for details."
userdata_pre_install: "Script to be ran before the GitHub Actions runner is installed on the EC2 instances"
userdata_post_install: "Script to be ran after the GitHub Actions runner is installed on the EC2 instances"
runner_hook_job_started: "Script to be ran in the runner environment at the beginning of every job"
runner_hook_job_completed: "Script to be ran in the runner environment at the end of every job"
runner_ec2_tags: "Map of tags that will be added to the launch template instance tag specifications."
runner_iam_role_managed_policy_arns: "Attach AWS or customer-managed IAM policies (by ARN) to the runner IAM role"
vpc_id: "The VPC for security groups of the action runners. If not set uses the value of `var.vpc_id`."
Expand Down
2 changes: 2 additions & 0 deletions modules/runners/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ yarn run dist
| <a name="input_runner_disable_default_labels"></a> [runner\_disable\_default\_labels](#input\_runner\_disable\_default\_labels) | Disable default labels for the runners (os, architecture and `self-hosted`). If enabled, the runner will only have the extra labels provided in `runner_extra_labels`. | `bool` | `false` | no |
| <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 specifications. | `map(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_hook_job_completed"></a> [runner\_hook\_job\_completed](#input\_runner\_hook\_job\_completed) | Script to be ran in the runner environment at the end of every job | `string` | `""` | no |
| <a name="input_runner_hook_job_started"></a> [runner\_hook\_job\_started](#input\_runner\_hook\_job\_started) | Script to be ran in the runner environment at the beginning of every job | `string` | `""` | 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 |
| <a name="input_runner_labels"></a> [runner\_labels](#input\_runner\_labels) | All the labels for the runners (GitHub) including the default one's(e.g: self-hosted, linux, x64, label1, label2). Separate each label by a comma | `list(string)` | n/a | yes |
| <a name="input_runner_log_files"></a> [runner\_log\_files](#input\_runner\_log\_files) | (optional) List of logfiles to send to CloudWatch, will only be used if `enable_cloudwatch_agent` is set to true. Object description: `log_group_name`: Name of the log group, `prefix_log_group`: If true, the log group name will be prefixed with `/github-self-hosted-runners/<var.prefix>`, `file_path`: path to the log file, `log_stream_name`: name of the log stream. | <pre>list(object({<br/> log_group_name = string<br/> prefix_log_group = bool<br/> file_path = string<br/> log_stream_name = string<br/> }))</pre> | `null` | no |
Expand Down
4 changes: 3 additions & 1 deletion modules/runners/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ locals {
S3_LOCATION_RUNNER_DISTRIBUTION = local.s3_location_runner_distribution
RUNNER_ARCHITECTURE = var.runner_architecture
})
post_install = var.userdata_post_install
post_install = var.userdata_post_install
hook_job_started = var.runner_hook_job_started
hook_job_completed = var.runner_hook_job_completed
start_runner = templatefile(local.userdata_start_runner[var.runner_os], {
metadata_tags = var.metadata_options != null ? var.metadata_options.instance_metadata_tags : "enabled"
})
Expand Down
16 changes: 16 additions & 0 deletions modules/runners/templates/user-data.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,20 @@ ${install_runner}

${post_install}

# Register runner job hooks
# Ref: https://github.com/actions/runner/blob/main/docs/adrs/1751-runner-job-hooks.md
%{ if hook_job_started != "" }
cat > /opt/actions-runner/hook_job_started.sh << EOF
${hook_job_started}
EOF
echo ACTIONS_RUNNER_HOOK_JOB_STARTED=/opt/actions-runner/hook_job_started.sh | tee -a /opt/actions-runner/.env
%{ endif }

%{ if hook_job_completed != "" }
cat > /opt/actions-runner/hook_job_completed.sh << EOF
${hook_job_completed}
EOF
echo ACTIONS_RUNNER_HOOK_JOB_COMPLETED=/opt/actions-runner/hook_job_completed.sh | tee -a /opt/actions-runner/.env
%{ endif }

${start_runner}
12 changes: 12 additions & 0 deletions modules/runners/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,18 @@ variable "userdata_post_install" {
default = ""
}

variable "runner_hook_job_started" {
description = "Script to be ran in the runner environment at the beginning of every job"
type = string
default = ""
}

variable "runner_hook_job_completed" {
description = "Script to be ran in the runner environment at the end of every job"
type = string
default = ""
}

variable "sqs_build_queue" {
description = "SQS queue to consume accepted build events."
type = object({
Expand Down
12 changes: 12 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,18 @@ variable "userdata_post_install" {
description = "Script to be ran after the GitHub Actions runner is installed on the EC2 instances"
}

variable "runner_hook_job_started" {
type = string
default = ""
description = "Script to be ran in the runner environment at the beginning of every job"
}

variable "runner_hook_job_completed" {
type = string
default = ""
description = "Script to be ran in the runner environment at the end of every job"
}

variable "idle_config" {
description = "List of time periods, defined as a cron expression, to keep a minimum amount of runners active instead of scaling down to 0. By defining this list you can ensure that in time periods that match the cron expression within 5 seconds a runner is kept idle."
type = list(object({
Expand Down
Loading