Skip to content

Commit b966604

Browse files
authored
upstream github-action-runners dockerhub authentication (cloudposse/terraform-aws-components#726)
1 parent a7c3a6f commit b966604

File tree

4 files changed

+114
-26
lines changed

4 files changed

+114
-26
lines changed

src/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,21 @@ components:
3737
github_app_id: "REPLACE_ME_GH_APP_ID"
3838
github_app_installation_id: "REPLACE_ME_GH_INSTALLATION_ID"
3939

40+
# use to enable docker config json secret, which can login to dockerhub for your GHA Runners
41+
docker_config_json_enabled: true
42+
# The content of this param should look like:
43+
# {
44+
# "auths": {
45+
# "https://index.docker.io/v1/": {
46+
# "username": "your_username",
47+
# "password": "your_password
48+
# "email": "your_email",
49+
# "auth": "$(echo "your_username:your_password" | base64)"
50+
# }
51+
# }
52+
# } | base64
53+
ssm_docker_config_json_path: "/github_runners/docker/config-json"
54+
4055
# ssm_github_webhook_secret_token_path: "/github_runners/github_webhook_secret_token"
4156
# The webhook based autoscaler is much more efficient than the polling based autoscaler
4257
webhook:
@@ -410,6 +425,7 @@ Consult [actions-runner-controller](https://github.com/actions-runner-controller
410425
| Name | Type |
411426
|------|------|
412427
| [aws_eks_cluster_auth.eks](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/eks_cluster_auth) | data source |
428+
| [aws_ssm_parameter.docker_config_json](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
413429
| [aws_ssm_parameter.github_token](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
414430
| [aws_ssm_parameter.github_webhook_secret_token](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
415431
@@ -430,6 +446,7 @@ Consult [actions-runner-controller](https://github.com/actions-runner-controller
430446
| <a name="input_create_namespace"></a> [create\_namespace](#input\_create\_namespace) | Create the namespace if it does not yet exist. Defaults to `false`. | `bool` | `null` | no |
431447
| <a name="input_delimiter"></a> [delimiter](#input\_delimiter) | Delimiter to be used between ID elements.<br>Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no |
432448
| <a name="input_descriptor_formats"></a> [descriptor\_formats](#input\_descriptor\_formats) | Describe additional descriptors to be output in the `descriptors` output map.<br>Map of maps. Keys are names of descriptors. Values are maps of the form<br>`{<br> format = string<br> labels = list(string)<br>}`<br>(Type is `any` so the map values can later be enhanced to provide additional options.)<br>`format` is a Terraform format string to be passed to the `format()` function.<br>`labels` is a list of labels, in order, to pass to `format()` function.<br>Label values will be normalized before being passed to `format()` so they will be<br>identical to how they appear in `id`.<br>Default is `{}` (`descriptors` output will be empty). | `any` | `{}` | no |
449+
| <a name="input_docker_config_json_enabled"></a> [docker\_config\_json\_enabled](#input\_docker\_config\_json\_enabled) | Whether the Docker config JSON is enabled | `bool` | `false` | no |
433450
| <a name="input_eks_component_name"></a> [eks\_component\_name](#input\_eks\_component\_name) | The name of the eks component | `string` | `"eks/cluster"` | no |
434451
| <a name="input_enabled"></a> [enabled](#input\_enabled) | Set to false to prevent the module from creating any resources | `bool` | `null` | no |
435452
| <a name="input_environment"></a> [environment](#input\_environment) | ID element. Usually used for region e.g. 'uw2', 'us-west-2', OR role 'prod', 'staging', 'dev', 'UAT' | `string` | `null` | no |
@@ -461,6 +478,7 @@ Consult [actions-runner-controller](https://github.com/actions-runner-controller
461478
| <a name="input_resources"></a> [resources](#input\_resources) | The cpu and memory of the deployment's limits and requests. | <pre>object({<br> limits = object({<br> cpu = string<br> memory = string<br> })<br> requests = object({<br> cpu = string<br> memory = string<br> })<br> })</pre> | n/a | yes |
462479
| <a name="input_runners"></a> [runners](#input\_runners) | Map of Action Runner configurations, with the key being the name of the runner. Please note that the name must be in<br>kebab-case.<br><br>For example:<pre>hcl<br>organization_runner = {<br> type = "organization" # can be either 'organization' or 'repository'<br> dind_enabled: false # A Docker sidecar container will be deployed<br> image: summerwind/actions-runner # If dind_enabled=true, set this to 'summerwind/actions-runner-dind'<br> scope = "ACME" # org name for Organization runners, repo name for Repository runners<br> group = "core-automation" # Optional. Assigns the runners to a runner group, for access control.<br> scale_down_delay_seconds = 300<br> min_replicas = 1<br> max_replicas = 5<br> busy_metrics = {<br> scale_up_threshold = 0.75<br> scale_down_threshold = 0.25<br> scale_up_factor = 2<br> scale_down_factor = 0.5<br> }<br> labels = [<br> "Ubuntu",<br> "core-automation",<br> ]<br>}</pre> | <pre>map(object({<br> type = string<br> scope = string<br> group = optional(string, null)<br> image = optional(string, "")<br> dind_enabled = bool<br> node_selector = optional(map(string), {})<br> pod_annotations = optional(map(string), {})<br> tolerations = optional(list(object({<br> key = string<br> operator = string<br> value = optional(string, null)<br> effect = string<br> })), [])<br> scale_down_delay_seconds = number<br> min_replicas = number<br> max_replicas = number<br> busy_metrics = optional(object({<br> scale_up_threshold = string<br> scale_down_threshold = string<br> scale_up_adjustment = optional(string)<br> scale_down_adjustment = optional(string)<br> scale_up_factor = optional(string)<br> scale_down_factor = optional(string)<br> }))<br> webhook_driven_scaling_enabled = bool<br> webhook_startup_timeout = optional(string, null)<br> pull_driven_scaling_enabled = bool<br> labels = list(string)<br> storage = optional(string, null)<br> pvc_enabled = optional(bool, false)<br> resources = object({<br> limits = object({<br> cpu = string<br> memory = string<br> ephemeral_storage = optional(string, null)<br> })<br> requests = object({<br> cpu = string<br> memory = string<br> })<br> })<br> }))</pre> | n/a | yes |
463480
| <a name="input_s3_bucket_arns"></a> [s3\_bucket\_arns](#input\_s3\_bucket\_arns) | List of ARNs of S3 Buckets to which the runners will have read-write access to. | `list(string)` | `[]` | no |
481+
| <a name="input_ssm_docker_config_json_path"></a> [ssm\_docker\_config\_json\_path](#input\_ssm\_docker\_config\_json\_path) | SSM path to the Docker config JSON | `string` | `null` | no |
464482
| <a name="input_ssm_github_secret_path"></a> [ssm\_github\_secret\_path](#input\_ssm\_github\_secret\_path) | The path in SSM to the GitHub app private key file contents or GitHub PAT token. | `string` | `""` | no |
465483
| <a name="input_ssm_github_webhook_secret_token_path"></a> [ssm\_github\_webhook\_secret\_token\_path](#input\_ssm\_github\_webhook\_secret\_token\_path) | The path in SSM to the GitHub Webhook Secret token. | `string` | `""` | no |
466484
| <a name="input_stage"></a> [stage](#input\_stage) | ID element. Usually used to indicate role, e.g. 'prod', 'staging', 'source', 'build', 'test', 'deploy', 'release' | `string` | `null` | no |

src/charts/actions-runner/templates/runnerdeployment.yaml

Lines changed: 71 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@ spec:
1818
# save space.
1919
storage: 100Gi
2020
{{- end }}
21+
{{- if .Values.docker_config_json_enabled }}
22+
---
23+
apiVersion: v1
24+
kind: Secret
25+
metadata:
26+
name: regcred
27+
type: kubernetes.io/dockerconfigjson
28+
data:
29+
.dockerconfigjson: {{ .Values.docker_config_json }}
30+
{{- end }}
2131
---
2232
apiVersion: actions.summerwind.dev/v1alpha1
2333
kind: RunnerDeployment
@@ -34,16 +44,32 @@ spec:
3444
{{- toYaml . | nindent 8 }}
3545
{{- end }}
3646
spec:
47+
{{- if .Values.docker_config_json_enabled }}
48+
# secrets volumeMount are always mounted readOnly so config.json has to be copied to the correct directory
49+
# https://github.com/kubernetes/kubernetes/issues/62099
50+
# https://github.com/actions/actions-runner-controller/issues/2123#issuecomment-1527077517
51+
52+
initContainers:
53+
- name: docker-config-writer
54+
image: {{ .Values.image | quote }}
55+
command: [ "sh", "-c", "cat /home/.docker/config.json > /home/runner/.docker/config.json" ]
56+
volumeMounts:
57+
- mountPath: /home/.docker/
58+
name: docker-secret
59+
- mountPath: /home/runner/.docker
60+
name: docker-config-volume
61+
{{- end }}
62+
3763
# As of 2023-03-31
3864
# Recommended by https://github.com/actions/actions-runner-controller/blob/master/docs/automatically-scaling-runners.md
3965
terminationGracePeriodSeconds: 100
4066
env:
41-
# RUNNER_GRACEFUL_STOP_TIMEOUT is the time the runner will give itself to try to finish
42-
# a job before it gracefully cancels itself in response to a pod termination signal.
43-
# It should be less than the terminationGracePeriodSeconds above so that it has time
44-
# to report its status and deregister itself from the runner pool.
45-
- name: RUNNER_GRACEFUL_STOP_TIMEOUT
46-
value: "90"
67+
# RUNNER_GRACEFUL_STOP_TIMEOUT is the time the runner will give itself to try to finish
68+
# a job before it gracefully cancels itself in response to a pod termination signal.
69+
# It should be less than the terminationGracePeriodSeconds above so that it has time
70+
# to report its status and deregister itself from the runner pool.
71+
- name: RUNNER_GRACEFUL_STOP_TIMEOUT
72+
value: "90"
4773

4874
# You could reserve nodes for runners by labeling and tainting nodes with
4975
# node-role.kubernetes.io/actions-runner
@@ -89,6 +115,10 @@ spec:
89115
dockerdWithinRunnerContainer: {{ .Values.dind_enabled }}
90116
image: {{ .Values.image | quote }}
91117
imagePullPolicy: IfNotPresent
118+
{{- if .Values.docker_config_json_enabled }}
119+
imagePullSecrets:
120+
- name: regcred
121+
{{- end }}
92122
serviceAccountName: {{ .Values.service_account_name }}
93123
resources:
94124
limits:
@@ -105,29 +135,47 @@ spec:
105135
{{- end }}
106136
{{- if and .Values.dind_enabled .Values.storage }}
107137
dockerVolumeMounts:
108-
- mountPath: /var/lib/docker
109-
name: docker-volume
138+
- mountPath: /var/lib/docker
139+
name: docker-volume
110140
{{- end }}
111-
{{- if .Values.pvc_enabled }}
141+
{{- if or (.Values.pvc_enabled) (.Values.docker_config_json_enabled) }}
112142
volumeMounts:
113-
- mountPath: /home/runner/work/shared
114-
name: shared-volume
143+
{{- if .Values.pvc_enabled }}
144+
- mountPath: /home/runner/work/shared
145+
name: shared-volume
146+
{{- end }}
147+
{{- if .Values.docker_config_json_enabled }}
148+
- mountPath: /home/.docker/
149+
name: docker-secret
150+
- mountPath: /home/runner/.docker
151+
name: docker-config-volume
152+
{{- end }}
115153
{{- end }}
116-
{{- if or (and .Values.dind_enabled .Values.storage) (.Values.pvc_enabled) }}
154+
{{- if or (and .Values.dind_enabled .Values.storage) (.Values.pvc_enabled) (.Values.docker_config_json_enabled) }}
117155
volumes:
118156
{{- if and .Values.dind_enabled .Values.storage }}
119-
- name: docker-volume
120-
ephemeral:
121-
volumeClaimTemplate:
122-
spec:
123-
accessModes: [ "ReadWriteOnce" ] # Only 1 pod can connect at a time
124-
resources:
125-
requests:
126-
storage: {{ .Values.storage }}
157+
- name: docker-volume
158+
ephemeral:
159+
volumeClaimTemplate:
160+
spec:
161+
accessModes: [ "ReadWriteOnce" ] # Only 1 pod can connect at a time
162+
resources:
163+
requests:
164+
storage: {{ .Values.storage }}
127165
{{- end }}
128166
{{- if .Values.pvc_enabled }}
129-
- name: shared-volume
130-
persistentVolumeClaim:
131-
claimName: {{ .Values.release_name }}
167+
- name: shared-volume
168+
persistentVolumeClaim:
169+
claimName: {{ .Values.release_name }}
132170
{{- end }}
171+
{{- if .Values.docker_config_json_enabled }}
172+
- name: docker-secret
173+
secret:
174+
secretName: regcred
175+
items:
176+
- key: .dockerconfigjson
177+
path: config.json
178+
- name: docker-config-volume
179+
emptyDir:
180+
{{- end }}
133181
{{- end }}

src/main.tf

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
locals {
22
enabled = module.this.enabled
33

4-
webhook_enabled = local.enabled ? try(var.webhook.enabled, false) : false
5-
webhook_host = local.webhook_enabled ? format(var.webhook.hostname_template, var.tenant, var.stage, var.environment) : "example.com"
6-
runner_groups_enabled = length(compact(values(var.runners)[*].group)) > 0
4+
webhook_enabled = local.enabled ? try(var.webhook.enabled, false) : false
5+
webhook_host = local.webhook_enabled ? format(var.webhook.hostname_template, var.tenant, var.stage, var.environment) : "example.com"
6+
runner_groups_enabled = length(compact(values(var.runners)[*].group)) > 0
7+
docker_config_json_enabled = local.enabled && var.docker_config_json_enabled
8+
docker_config_json = one(data.aws_ssm_parameter.docker_config_json[*].value)
79

810
github_app_enabled = length(var.github_app_id) > 0 && length(var.github_app_installation_id) > 0
911
create_secret = local.enabled && length(var.existing_kubernetes_secret_name) == 0
@@ -100,6 +102,12 @@ data "aws_ssm_parameter" "github_webhook_secret_token" {
100102
with_decryption = true
101103
}
102104

105+
data "aws_ssm_parameter" "docker_config_json" {
106+
count = local.docker_config_json_enabled ? 1 : 0
107+
name = var.ssm_docker_config_json_path
108+
with_decryption = true
109+
}
110+
103111
module "actions_runner_controller" {
104112
source = "cloudposse/helm-release/aws"
105113
version = "0.7.0"
@@ -225,6 +233,8 @@ module "actions_runner" {
225233
pvc_enabled = each.value.pvc_enabled
226234
node_selector = each.value.node_selector
227235
tolerations = each.value.tolerations
236+
docker_config_json_enabled = local.docker_config_json_enabled
237+
docker_config_json = local.docker_config_json
228238
}),
229239
each.value.group == null ? "" : yamlencode({ group = each.value.group }),
230240
local.busy_metrics_filtered[each.key] == null ? "" : yamlencode(local.busy_metrics_filtered[each.key]),

src/variables.tf

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,18 @@ variable "s3_bucket_arns" {
131131
default = []
132132
}
133133

134+
variable "docker_config_json_enabled" {
135+
type = bool
136+
description = "Whether the Docker config JSON is enabled"
137+
default = false
138+
}
139+
140+
variable "ssm_docker_config_json_path" {
141+
type = string
142+
description = "SSM path to the Docker config JSON"
143+
default = null
144+
}
145+
134146
variable "runners" {
135147
description = <<-EOT
136148
Map of Action Runner configurations, with the key being the name of the runner. Please note that the name must be in

0 commit comments

Comments
 (0)