Skip to content

Commit

Permalink
feat(runners): add support to disable default labels (Linux) (#3491)
Browse files Browse the repository at this point in the history
In case the pool of runners deployed using this module are fully
available for an wide GH org (no repository restrictions) any workflow
configured to run in runners that contain just default labels in the
runs-on definition:

e.g.

`runs-on: self-hosted`
`runs-on: Linux`

can end-up running in this pool without knowing it. That's why I have
decided to remove the default labels from our runners and just rely on
unique custom labels, and I believed the best way to do it by adding a
runner_enable_default_labels variable - that by default is true (so, it
doesn't change the current behavior), but it can help other people to
deal with similar issues like the one described above.

---------

Co-authored-by: Niek Palm <[email protected]>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Niek Palm <[email protected]>
  • Loading branch information
4 people authored Nov 18, 2024
1 parent 78b910e commit 772e1a5
Show file tree
Hide file tree
Showing 11 changed files with 55 additions and 12 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,10 @@ Talk to the forestkeepers in the `runners-channel` on Slack.
| <a name="input_runner_binaries_syncer_lambda_zip"></a> [runner\_binaries\_syncer\_lambda\_zip](#input\_runner\_binaries\_syncer\_lambda\_zip) | File location of the binaries sync lambda zip file. | `string` | `null` | no |
| <a name="input_runner_boot_time_in_minutes"></a> [runner\_boot\_time\_in\_minutes](#input\_runner\_boot\_time\_in\_minutes) | The minimum time for an EC2 runner to boot and register as a runner. | `number` | `5` | no |
| <a name="input_runner_credit_specification"></a> [runner\_credit\_specification](#input\_runner\_credit\_specification) | The credit option for CPU usage of a T instance. Can be unset, "standard" or "unlimited". | `string` | `null` | no |
| <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`. In case you on own start script is used, this configuration parameter needs to be parsed via SSM. For Windows no support yet. | `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_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). Labels checks on the webhook can be enforced by setting `enable_runner_workflow_job_labels_check_all`. GitHub read-only labels should not be provided. | `list(string)` | `[]` | 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_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 |
Expand Down
15 changes: 9 additions & 6 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ To be able to support a number of use-cases, the module has quite a lot of confi
- GitHub Cloud vs GitHub Enterprise Server (GHES). The runners support GitHub Cloud as well GitHub Enterprise Server. For GHES, we rely on our community for support and testing. We at Philips have no capability to test GHES ourselves.
- Spot vs on-demand. The runners use either the EC2 spot or on-demand life cycle. Runners will be created via the AWS [CreateFleet API](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateFleet.html). The module (scale up lambda) will request via the CreateFleet API to create instances in one of the subnets and of the specified instance types.
- ARM64 support via Graviton/Graviton2 instance-types. When using the default example or top-level module, specifying `instance_types` that match a Graviton/Graviton 2 (ARM64) architecture (e.g. a1, t4g or any 6th-gen `g` or `gd` type), you must also specify `runner_architecture = "arm64"` and the sub-modules will be automatically configured to provision with ARM64 AMIs and leverage GitHub's ARM64 action runner. See below for more details.
- Disable default labels for the runners (os, architecture and `self-hosted`) can achieve by setting `runner_disable_default_labels` = true. If enabled, the runner will only have the extra labels provided in `runner_extra_labels`. In case you on own start script is used, this configuration parameter needs to be parsed via SSM. For Windows no support yet.

## AWS SSM Parameters

Expand All @@ -24,14 +25,16 @@ The module uses the AWS System Manager Parameter Store to store configuration fo
| `ssm_paths.root/var.prefix?/runners/config/<name>` | Configuration parameters used by runner start script |
| `ssm_paths.root/var.prefix?/runners/tokens/<ec2-instance-id>` | Either JIT configuration (ephemeral runners) or registration tokens (non ephemeral runners) generated by the control plane (scale-up lambda), and consumed by the start script on the runner to activate / register the runner. |
| `ssm_paths.root/var.prefix?/webhook/runner-matcher-config` | Runner matcher config used by webhook to decide the target for the webhook event. |

Available configuration parameters:

| Parameter name | Description |
| ------------------- | ----------------------------------------------------------- |
| `agent_mode` | Indicates if the agent is running in ephemeral mode or not. |
| `enable_cloudwatch` | Configuration for the cloudwatch agent to stream logging. |
| `run_as` | The user used for running the GitHub action runner agent. |
| `token_path` | The path where tokens are stored. |
| Parameter name | Description |
|-------------------------------------|---------------------------------------------------------------------------------------------------|
| `agent_mode` | Indicates if the agent is running in ephemeral mode or not. |
| `disable_default_labels` | Indicates if the default labels for the runners (os, architecture and `self-hosted`) are disabled |
| `enable_cloudwatch` | Configuration for the cloudwatch agent to stream logging. |
| `run_as` | The user used for running the GitHub action runner agent. |
| `token_path` | The path where tokens are stored. |

## Encryption

Expand Down
4 changes: 3 additions & 1 deletion main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ locals {
key_base64 = module.ssm.parameters.github_app_key_base64
}

runner_labels = sort(distinct(concat(["self-hosted", var.runner_os, var.runner_architecture], var.runner_extra_labels)))
default_runner_labels = distinct(concat(["self-hosted", var.runner_os, var.runner_architecture]))
runner_labels = (var.runner_disable_default_labels == false) ? sort(concat(local.default_runner_labels, var.runner_extra_labels)) : var.runner_extra_labels

ssm_root_path = var.ssm_paths.use_prefix ? "/${var.ssm_paths.root}/${var.prefix}" : "/${var.ssm_paths.root}"
}
Expand Down Expand Up @@ -199,6 +200,7 @@ module "runners" {
scale_down_schedule_expression = var.scale_down_schedule_expression
minimum_running_time_in_minutes = var.minimum_running_time_in_minutes
runner_boot_time_in_minutes = var.runner_boot_time_in_minutes
runner_disable_default_labels = var.runner_disable_default_labels
runner_labels = local.runner_labels
runner_as_root = var.runner_as_root
runner_run_as = var.runner_run_as
Expand Down
2 changes: 1 addition & 1 deletion modules/multi-runner/README.md

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion modules/multi-runner/runners.tf
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ module "runners" {
scale_down_schedule_expression = each.value.runner_config.scale_down_schedule_expression
minimum_running_time_in_minutes = each.value.runner_config.minimum_running_time_in_minutes
runner_boot_time_in_minutes = each.value.runner_config.runner_boot_time_in_minutes
runner_labels = sort(distinct(concat(["self-hosted", each.value.runner_config.runner_os, each.value.runner_config.runner_architecture], each.value.runner_config.runner_extra_labels)))
runner_disable_default_labels = each.value.runner_config.runner_disable_default_labels
runner_labels = each.value.runner_config.runner_disable_default_labels ? sort(distinct(each.value.runner_config.runner_extra_labels)) : sort(distinct(concat(["self-hosted", each.value.runner_config.runner_os, each.value.runner_config.runner_architecture], each.value.runner_config.runner_extra_labels)))
runner_as_root = each.value.runner_config.runner_as_root
runner_run_as = each.value.runner_config.runner_run_as
runners_maximum_count = each.value.runner_config.runners_maximum_count
Expand Down
2 changes: 2 additions & 0 deletions modules/multi-runner/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ variable "multi_runner_config" {
pool_runner_owner = optional(string, null)
runner_as_root = optional(bool, false)
runner_boot_time_in_minutes = optional(number, 5)
runner_disable_default_labels = optional(bool, false)
runner_extra_labels = optional(list(string), [])
runner_group_name = optional(string, "Default")
runner_name_prefix = optional(string, "")
Expand Down Expand Up @@ -164,6 +165,7 @@ variable "multi_runner_config" {
runner_additional_security_group_ids: "List of additional security groups IDs to apply to the runner. If added outside the multi_runner_config block, the additional security group(s) will be applied to all runner configs. If added inside the multi_runner_config, the additional security group(s) will be applied to the individual runner."
runner_as_root: "Run the action runner under the root user. Variable `runner_run_as` will be ignored."
runner_boot_time_in_minutes: "The minimum time for an EC2 runner to boot and register as a runner."
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`. In case you on own start script is used, this configuration parameter needs to be parsed via SSM. For Windows no support yet."
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 `multi_runner_config.matcherConfig.exactMatch`. GitHub read-only labels should not be provided."
runner_group_name: "Name of the runner group."
runner_name_prefix: "Prefix for the GitHub runner name."
Expand Down
2 changes: 2 additions & 0 deletions modules/runners/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ yarn run dist
| [aws_launch_template.runner](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_template) | resource |
| [aws_security_group.runner_sg](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
| [aws_ssm_parameter.cloudwatch_agent_config_runner](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
| [aws_ssm_parameter.disable_default_labels](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
| [aws_ssm_parameter.jit_config_enabled](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
| [aws_ssm_parameter.runner_agent_mode](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
| [aws_ssm_parameter.runner_config_run_as](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
Expand Down Expand Up @@ -199,6 +200,7 @@ yarn run dist
| <a name="input_runner_architecture"></a> [runner\_architecture](#input\_runner\_architecture) | The platform architecture of the runner instance\_type. | `string` | `"x64"` | no |
| <a name="input_runner_as_root"></a> [runner\_as\_root](#input\_runner\_as\_root) | Run the action runner under the root user. Variable `runner_run_as` will be ignored. | `bool` | `false` | no |
| <a name="input_runner_boot_time_in_minutes"></a> [runner\_boot\_time\_in\_minutes](#input\_runner\_boot\_time\_in\_minutes) | The minimum time for an EC2 runner to boot and register as a runner. | `number` | `5` | no |
| <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_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
7 changes: 7 additions & 0 deletions modules/runners/runner-config.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ resource "aws_ssm_parameter" "runner_agent_mode" {
tags = local.tags
}

resource "aws_ssm_parameter" "disable_default_labels" {
name = "${var.ssm_paths.root}/${var.ssm_paths.config}/disable_default_labels"
type = "String"
value = var.runner_disable_default_labels
tags = local.tags
}

resource "aws_ssm_parameter" "jit_config_enabled" {
name = "${var.ssm_paths.root}/${var.ssm_paths.config}/enable_jit_config"
type = "String"
Expand Down
10 changes: 9 additions & 1 deletion modules/runners/templates/start-runner.sh
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ echo "Retrieved /$ssm_config_path/enable_cloudwatch parameter - ($enable_cloudwa
agent_mode=$(echo "$parameters" | jq --arg ssm_config_path "$ssm_config_path" -r '.[] | select(.Name == "'$ssm_config_path'/agent_mode") | .Value')
echo "Retrieved /$ssm_config_path/agent_mode parameter - ($agent_mode)"

disable_default_labels=$(echo "$parameters" | jq --arg ssm_config_path "$ssm_config_path" -r '.[] | select(.Name == "'$ssm_config_path'/disable_default_labels") | .Value')
echo "Retrieved /$ssm_config_path/disable_default_labels parameter - ($disable_default_labels)"

enable_jit_config=$(echo "$parameters" | jq --arg ssm_config_path "$ssm_config_path" -r '.[] | select(.Name == "'$ssm_config_path'/enable_jit_config") | .Value')
echo "Retrieved /$ssm_config_path/enable_jit_config parameter - ($enable_jit_config)"

Expand Down Expand Up @@ -216,7 +219,12 @@ echo "Starting the runner as user $run_as"
# configure the runner if the runner is non ephemeral or jit config is disabled
if [[ "$enable_jit_config" == "false" || $agent_mode != "ephemeral" ]]; then
echo "Configure GH Runner as user $run_as"
sudo --preserve-env=RUNNER_ALLOW_RUNASROOT -u "$run_as" -- ./config.sh --unattended --name "$runner_name_prefix$instance_id" --work "_work" $${config}
if [[ "$disable_default_labels" == "true" ]]; then
extra_flags="--no-default-labels"
else
extra_flags=""
fi
sudo --preserve-env=RUNNER_ALLOW_RUNASROOT -u "$run_as" -- ./config.sh $${extra_flags} --unattended --name "$runner_name_prefix$instance_id" --work "_work" $${config}
fi

create_xray_success_segment "$SEGMENT"
Expand Down
6 changes: 6 additions & 0 deletions modules/runners/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,12 @@ variable "runner_boot_time_in_minutes" {
default = 5
}

variable "runner_disable_default_labels" {
description = "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`."
type = bool
default = false
}

variable "runner_labels" {
description = "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"
type = list(string)
Expand Down
13 changes: 12 additions & 1 deletion variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,21 @@ variable "runner_boot_time_in_minutes" {
default = 5
}

variable "runner_disable_default_labels" {
description = "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`. In case you on own start script is used, this configuration parameter needs to be parsed via SSM. For Windows no support yet."
type = bool
default = false
}

variable "runner_extra_labels" {
description = "Extra (custom) labels for the runners (GitHub). Labels checks on the webhook can be enforced by setting `enable_runner_workflow_job_labels_check_all`. GitHub read-only labels should not be provided."
description = "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."
type = list(string)
default = []

validation {
condition = var.runner_extra_labels != null
error_message = "Extra labels should not be null."
}
}

variable "runner_group_name" {
Expand Down

0 comments on commit 772e1a5

Please sign in to comment.