diff --git a/charts/gha-runner-scale-set-dev/.helmignore b/charts/gha-runner-scale-set-dev/.helmignore new file mode 100644 index 00000000..faeb926b --- /dev/null +++ b/charts/gha-runner-scale-set-dev/.helmignore @@ -0,0 +1,24 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ +tests/ diff --git a/charts/gha-runner-scale-set-dev/Chart.yaml b/charts/gha-runner-scale-set-dev/Chart.yaml new file mode 100644 index 00000000..a87dfdbe --- /dev/null +++ b/charts/gha-runner-scale-set-dev/Chart.yaml @@ -0,0 +1,33 @@ +apiVersion: v2 +name: gha-runner-scale-set +description: A Helm chart for deploying an AutoScalingRunnerSet + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: "0.14.0" + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.14.0" + +home: https://github.com/actions/actions-runner-controller + +sources: + - "https://github.com/actions/actions-runner-controller" + +maintainers: + - name: actions + url: https://github.com/actions diff --git a/charts/gha-runner-scale-set-dev/templates/_defaults.tpl b/charts/gha-runner-scale-set-dev/templates/_defaults.tpl new file mode 100644 index 00000000..7a68bd90 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/templates/_defaults.tpl @@ -0,0 +1,108 @@ +{{- define "autoscaling-runner-set.name" -}} +{{- $name := .Values.runnerScaleSetName | default .Release.Name | replace "_" "-" | trimSuffix "-" }} +{{- if or (empty $name) (gt (len $name) 45) }} + {{ fail "Autoscaling runner set name must have up to 45 characters" }} +{{- end }} +{{- $name }} +{{- end }} + +{{- define "autoscaling-runner-set.namespace" -}} +{{- .Values.namespaceOverride | default .Release.Namespace -}} +{{- end }} + +{{/* +The name of the manager Role. +*/}} +{{- define "manager-role.name" -}} +{{- printf "%s-manager-role" (include "autoscaling-runner-set.name" .) -}} +{{- end }} + +{{/* +The name of the GitHub secret used for authentication. +*/}} +{{- define "github-secret.name" -}} +{{- if not (empty .Values.auth.secretName) -}} + {{- .Values.auth.secretName -}} +{{- else -}} + {{- include "autoscaling-runner-set.name" . }}-github-secret +{{- end -}} +{{- end }} + +{{/* +The name of the no-permission ServiceAccount. + +This ServiceAccount is intended for non-kubernetes runner modes when the user +has not specified an explicit ServiceAccount. +*/}} +{{- define "no-permission-serviceaccount.name" -}} +{{- printf "%s-no-permission" (include "autoscaling-runner-set.name" .) -}} +{{- end }} + +{{/* +The name of the kubernetes-mode Role. + +Kept intentionally aligned with the legacy chart behavior. +*/}} +{{- define "kube-mode-role.name" -}} +{{- printf "%s-kube-mode" (include "autoscaling-runner-set.name" .) -}} +{{- end }} + + +{{/* +The name of the kubernetes-mode RoleBinding. + +Kept intentionally aligned with the kubernetes-mode Role name. +*/}} +{{- define "kube-mode-role-binding.name" -}} +{{- include "kube-mode-role.name" . -}} +{{- end }} + + +{{/* +The name of the kubernetes-mode ServiceAccount. + +Kept intentionally aligned with the legacy chart behavior. +*/}} +{{- define "kube-mode-serviceaccount.name" -}} +{{- include "kube-mode-role.name" . -}} +{{- end }} + +{{/* +Create the common labels used across all resources. +*/}} +{{- define "gha-common-labels" -}} +helm.sh/chart: {{ include "gha-runner-scale-set.chart" . }} +app.kubernetes.io/name: {{ include "autoscaling-runner-set.name" . }} +app.kubernetes.io/instance: {{ include "autoscaling-runner-set.name" . }} +app.kubernetes.io/version: {{ .Chart.AppVersion }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/part-of: "gha-rs" +actions.github.com/scale-set-name: {{ include "autoscaling-runner-set.name" . }} +actions.github.com/scale-set-namespace: {{ include "autoscaling-runner-set.namespace" . }} +{{- end }} + + + +{{- define "runner.image" -}} +{{- $runner := .Values.runner.container | default dict -}} +{{- if not (kindIs "map" $runner) -}} + {{- fail "runner.container must be a map/object" -}} +{{- end -}} +{{- $image := $runner.image | default "ghcr.io/actions/actions-runner:latest" -}} +{{- if not (kindIs "string" $image) -}} + {{- fail "runner.container.image must be a string" -}} +{{- end -}} +{{- $image }} +{{- end }} + +{{- define "runner.command" -}} +{{- $runner := .Values.runner.container | default dict -}} +{{- if not (kindIs "map" $runner) -}} + {{- fail "runner.container must be a map/object" -}} +{{- end -}} +{{- $command := $runner.command | default (list "/home/runner/run.sh") -}} +{{- if not (kindIs "slice" $command) -}} + {{- fail "runner.container.command must be a list/array" -}} +{{- end -}} +{{- toJson $command -}} +{{- end }} \ No newline at end of file diff --git a/charts/gha-runner-scale-set-dev/templates/_helpers.tpl b/charts/gha-runner-scale-set-dev/templates/_helpers.tpl new file mode 100644 index 00000000..89cc49ed --- /dev/null +++ b/charts/gha-runner-scale-set-dev/templates/_helpers.tpl @@ -0,0 +1,365 @@ +{{/* +Create the labels for the manager Role. +*/}} +{{- define "manager-role.labels" -}} +{{- $resourceLabels := dict "app.kubernetes.io/component" "manager-role" -}} +{{- $commonLabels := include "gha-common-labels" . | fromYaml -}} +{{- $userLabels := include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.managerRole.metadata.labels | default (dict)) | fromYaml -}} +{{- $global := include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.all.metadata.labels | default (dict)) | fromYaml -}} +{{- toYaml (mergeOverwrite $global $userLabels $resourceLabels $commonLabels) }} +{{- end }} + + +{{/* +Create the annotations for the manager Role. + +Order of precedence: +1) resource.all.metadata.annotations +2) resource.managerRole.metadata.annotations +Reserved annotations are excluded from both levels. +*/}} +{{- define "manager-role.annotations" -}} +{{- $global := (include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.all.metadata.annotations | default (dict))) | fromYaml -}} +{{- $resource := (include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.managerRole.metadata.annotations | default (dict))) | fromYaml -}} +{{- $annotations := mergeOverwrite $global $resource -}} +{{- if not (empty $annotations) -}} + {{- toYaml $annotations }} +{{- end }} +{{- end }} + + +{{/* +The name of the manager RoleBinding. + +Kept intentionally aligned with the manager Role name, mirroring the legacy +chart behavior. +*/}} +{{- define "manager-role-binding.name" -}} +{{- include "manager-role.name" . -}} +{{- end }} + + +{{/* +Create the labels for the manager RoleBinding. +*/}} +{{- define "manager-role-binding.labels" -}} +{{- $resourceLabels := dict "app.kubernetes.io/component" "manager-role-binding" -}} +{{- $commonLabels := include "gha-common-labels" . | fromYaml -}} +{{- $userLabels := include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.managerRoleBinding.metadata.labels | default (dict)) | fromYaml -}} +{{- $global := include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.all.metadata.labels | default (dict)) | fromYaml -}} +{{- toYaml (mergeOverwrite $global $userLabels $resourceLabels $commonLabels) }} +{{- end }} + + +{{/* +Create the annotations for the manager RoleBinding. + +Order of precedence: +1) resource.all.metadata.annotations +2) resource.managerRoleBinding.metadata.annotations +Reserved annotations are excluded from both levels. +*/}} +{{- define "manager-role-binding.annotations" -}} +{{- $global := (include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.all.metadata.annotations | default (dict))) | fromYaml -}} +{{- $resource := (include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.managerRoleBinding.metadata.annotations | default (dict))) | fromYaml -}} +{{- $annotations := mergeOverwrite $global $resource -}} +{{- if not (empty $annotations) -}} + {{- toYaml $annotations }} +{{- end }} +{{- end }} + +{{/* +Create the labels for the GitHub auth secret. +*/}} +{{- define "github-secret.labels" -}} +{{- $resourceLabels := dict "app.kubernetes.io/component" "github-secret" -}} +{{- $commonLabels := include "gha-common-labels" . | fromYaml -}} +{{- $global := include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.all.metadata.labels | default (dict)) | fromYaml -}} +{{- toYaml (mergeOverwrite $global $resourceLabels $commonLabels) }} +{{- end }} + + +{{/* +Create the annotations for the GitHub auth secret. + +Only global annotations are applied. +Reserved annotations are excluded. +*/}} +{{- define "github-secret.annotations" -}} +{{- $annotations := (include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.all.metadata.annotations | default (dict))) | fromYaml -}} +{{- if not (empty $annotations) -}} + {{- toYaml $annotations }} +{{- end }} +{{- end }} + +{{/* +Create the labels for the no-permission ServiceAccount. +*/}} +{{- define "no-permission-serviceaccount.labels" -}} +{{- $resourceLabels := dict "app.kubernetes.io/component" "no-permission-serviceaccount" -}} +{{- $commonLabels := include "gha-common-labels" . | fromYaml -}} +{{- $userLabels := include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.noPermissionServiceAccount.metadata.labels | default (dict)) | fromYaml -}} +{{- $global := include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.all.metadata.labels | default (dict)) | fromYaml -}} +{{- toYaml (mergeOverwrite $global $userLabels $resourceLabels $commonLabels) }} +{{- end }} + + +{{/* +Create the annotations for the no-permission ServiceAccount. + +Order of precedence: +1) resource.all.metadata.annotations +2) resource.noPermissionServiceAccount.metadata.annotations +Reserved annotations are excluded from both levels. +*/}} +{{- define "no-permission-serviceaccount.annotations" -}} +{{- $global := (include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.all.metadata.annotations | default (dict))) | fromYaml -}} +{{- $resource := (include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.noPermissionServiceAccount.metadata.annotations | default (dict))) | fromYaml -}} +{{- $annotations := mergeOverwrite $global $resource -}} +{{- if not (empty $annotations) -}} + {{- toYaml $annotations }} +{{- end }} +{{- end }} + + +{{/* +Create the labels for the kubernetes-mode RoleBinding. +*/}} +{{- define "kube-mode-role-binding.labels" -}} +{{- $resourceLabels := dict "app.kubernetes.io/component" "kube-mode-role-binding" -}} +{{- $commonLabels := include "gha-common-labels" . | fromYaml -}} +{{- $userLabels := include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.kubernetesModeRoleBinding.metadata.labels | default (dict)) | fromYaml -}} +{{- $global := include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.all.metadata.labels | default (dict)) | fromYaml -}} +{{- toYaml (mergeOverwrite $global $userLabels $resourceLabels $commonLabels) }} +{{- end }} + + +{{/* +Create the annotations for the kubernetes-mode RoleBinding. + +Order of precedence: +1) resource.all.metadata.annotations +2) resource.kubernetesModeRoleBinding.metadata.annotations +Reserved annotations are excluded from both levels. +*/}} +{{- define "kube-mode-role-binding.annotations" -}} +{{- $global := (include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.all.metadata.annotations | default (dict))) | fromYaml -}} +{{- $resource := (include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.kubernetesModeRoleBinding.metadata.annotations | default (dict))) | fromYaml -}} +{{- $annotations := mergeOverwrite $global $resource -}} +{{- if not (empty $annotations) -}} + {{- toYaml $annotations }} +{{- end }} +{{- end }} + + +{{/* +Create the labels for the kubernetes-mode Role. +*/}} +{{- define "kube-mode-role.labels" -}} +{{- $resourceLabels := dict "app.kubernetes.io/component" "kube-mode-role" -}} +{{- $commonLabels := include "gha-common-labels" . | fromYaml -}} +{{- $userLabels := include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.kubernetesModeRole.metadata.labels | default (dict)) | fromYaml -}} +{{- $global := include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.all.metadata.labels | default (dict)) | fromYaml -}} +{{- toYaml (mergeOverwrite $global $userLabels $resourceLabels $commonLabels) }} +{{- end }} + + +{{/* +Create the annotations for the kubernetes-mode Role. + +Order of precedence: +1) resource.all.metadata.annotations +2) resource.kubernetesModeRole.metadata.annotations +Reserved annotations are excluded from both levels. +*/}} +{{- define "kube-mode-role.annotations" -}} +{{- $global := (include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.all.metadata.annotations | default (dict))) | fromYaml -}} +{{- $resource := (include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.kubernetesModeRole.metadata.annotations | default (dict))) | fromYaml -}} +{{- $annotations := mergeOverwrite $global $resource -}} +{{- if not (empty $annotations) -}} + {{- toYaml $annotations }} +{{- end }} +{{- end }} + + +{{/* +Create the labels for the kubernetes-mode ServiceAccount. +*/}} +{{- define "kube-mode-serviceaccount.labels" -}} +{{- $resourceLabels := dict "app.kubernetes.io/component" "kube-mode-serviceaccount" -}} +{{- $commonLabels := include "gha-common-labels" . | fromYaml -}} +{{- $userLabels := include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.kubernetesModeServiceAccount.metadata.labels | default (dict)) | fromYaml -}} +{{- $global := include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.all.metadata.labels | default (dict)) | fromYaml -}} +{{- toYaml (mergeOverwrite $global $userLabels $resourceLabels $commonLabels) }} +{{- end }} + + +{{/* +Create the annotations for the kubernetes-mode ServiceAccount. + +Order of precedence: +1) resource.all.metadata.annotations +2) resource.kubernetesModeServiceAccount.metadata.annotations +Reserved annotations are excluded from both levels. +*/}} +{{- define "kube-mode-serviceaccount.annotations" -}} +{{- $global := (include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.all.metadata.annotations | default (dict))) | fromYaml -}} +{{- $resource := (include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.kubernetesModeServiceAccount.metadata.annotations | default (dict))) | fromYaml -}} +{{- $annotations := mergeOverwrite $global $resource -}} +{{- if not (empty $annotations) -}} + {{- toYaml $annotations }} +{{- end }} +{{- end }} + + +{{/* +Create the labels for the autoscaling runner set. +*/}} +{{- define "autoscaling-runner-set.labels" -}} +{{- $resourceLabels := dict "app.kubernetes.io/component" "autoscaling-runner-set" -}} +{{- $commonLabels := include "gha-common-labels" . | fromYaml -}} +{{- $userLabels := include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.autoscalingRunnerSet.metadata.labels | default (dict)) | fromYaml -}} +{{- $global := include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.all.metadata.labels | default (dict)) | fromYaml -}} +{{- toYaml (mergeOverwrite $global $userLabels $resourceLabels $commonLabels) }} +{{- end }} + +{{/* +Takes a map of user labels and removes the ones with "actions.github.com/" prefix +*/}} +{{- define "apply-non-reserved-gha-labels-and-annotations" -}} +{{- $userLabels := . -}} +{{- $processed := dict -}} +{{- range $key, $value := $userLabels -}} + {{- if not (hasPrefix "actions.github.com/" $key) -}} + {{- $_ := set $processed $key $value -}} + {{- end -}} +{{- end -}} +{{- if not (empty $processed) -}} + {{- $processed | toYaml }} +{{- end }} +{{- end }} + +{{/* +Create the annotations for the autoscaling runner set. + +Order of precedence: +1) resource.all.metadata.annotations +2) resource.autoscalingRunnerSet.metadata.annotations +Reserved annotations are excluded from both levels. +*/}} +{{- define "autoscaling-runner-set.annotations" -}} +{{- $global := (include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.all.metadata.annotations | default (dict))) | fromYaml -}} +{{- $resource := (include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.autoscalingRunnerSet.metadata.annotations | default (dict))) | fromYaml -}} +{{- $annotations := mergeOverwrite $global $resource -}} +{{- if not (empty $annotations) -}} + {{- toYaml $annotations }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "gha-runner-scale-set.chart" -}} +{{- printf "gha-rs-%s" .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Container spec that is expanded for the runner container +*/}} +{{- define "autoscaling-runner-set.template-runner-container" -}} +{{- if not .Values.runner.container }} + {{ fail "You must provide a runner container specification in values.runner.container" }} +{{- end }} +{{- $tlsConfig := (default (dict) .Values.githubServerTLS) -}} +name: runner +image: {{ .Values.runner.container.image | default "ghcr.io/actions/runner:latest" }} +command: {{ toJson (default (list "/home/runner/run.sh") .Values.runner.container.command) }} +{{- $extra := omit .Values.runner.container "name" "image" "command" -}} +{{- if not (empty $extra) -}} +{{ toYaml $extra }} +{{- end -}} +{{- end }} + +{{- define "autoscaling-runner-set.template-service-account" -}} +{{- $runner := (.Values.runner | default dict) -}} +{{- $runnerMode := (index $runner "mode" | default "") -}} +{{- $kubeMode := (index $runner "kubernetesMode" | default dict) -}} +{{- $kubeServiceAccountName := (index $kubeMode "serviceAccountName" | default "") -}} +{{- $kubeDefaults := (index $kubeMode "default" | default true) -}} +{{- if ne $runnerMode "kubernetes" }} + {{- include "no-permission-serviceaccount.name" . }} +{{- else if not (empty $kubeServiceAccountName) }} + {{- $kubeServiceAccountName }} +{{- else if $kubeDefaults }} + {{- include "kube-mode-serviceaccount.name" . }} +{{- else }} + {{- fail "runner.kubernetesMode.serviceAccountName must be set when runner.mode is 'kubernetes' and runner.kubernetesMode.default is false" -}} +{{- end }} + +{{- end }} + +{{/* +Create labels for the runner Pod template (spec.template.metadata.labels). + +Order of precedence: +1) resource.all.metadata.labels +2) runner.pod.metadata.labels +3) common labels (cannot be overridden) + +Reserved actions.github.com/* labels are excluded from user/global inputs. +*/}} +{{- define "autoscaling-runner-set.runner-pod.labels" -}} +{{- $runner := (.Values.runner | default dict) -}} +{{- $pod := (index $runner "pod" | default dict) -}} +{{- if not (kindIs "map" $pod) -}} + {{- fail ".Values.runner.pod must be a map/object" -}} +{{- end -}} +{{- $podMetadata := (index $pod "metadata" | default dict) -}} +{{- if not (kindIs "map" $podMetadata) -}} + {{- fail ".Values.runner.pod.metadata must be a map/object" -}} +{{- end -}} +{{- $userRaw := (index $podMetadata "labels" | default (dict)) -}} +{{- if not (kindIs "map" $userRaw) -}} + {{- fail ".Values.runner.pod.metadata.labels must be a map/object" -}} +{{- end -}} +{{- $global := include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.all.metadata.labels | default (dict)) | fromYaml -}} +{{- $user := include "apply-non-reserved-gha-labels-and-annotations" $userRaw | fromYaml -}} +{{- $common := include "gha-common-labels" . | fromYaml -}} +{{- $labels := mergeOverwrite $global $user $common -}} +{{- if not (empty $labels) -}} + {{- toYaml $labels -}} +{{- end -}} +{{- end }} + +{{/* +Create annotations for the runner Pod template (spec.template.metadata.annotations). + +Order of precedence: +1) resource.all.metadata.annotations +2) runner.pod.metadata.annotations + +Reserved actions.github.com/* annotations are excluded from user/global inputs. +*/}} +{{- define "autoscaling-runner-set.runner-pod.annotations" -}} +{{- $runner := (.Values.runner | default dict) -}} +{{- $pod := (index $runner "pod" | default dict) -}} +{{- if not (kindIs "map" $pod) -}} + {{- fail ".Values.runner.pod must be a map/object" -}} +{{- end -}} +{{- $podMetadata := (index $pod "metadata" | default dict) -}} +{{- if not (kindIs "map" $podMetadata) -}} + {{- fail ".Values.runner.pod.metadata must be a map/object" -}} +{{- end -}} +{{- $userRaw := (index $podMetadata "annotations" | default (dict)) -}} +{{- if not (kindIs "map" $userRaw) -}} + {{- fail ".Values.runner.pod.metadata.annotations must be a map/object" -}} +{{- end -}} +{{- $global := (include "apply-non-reserved-gha-labels-and-annotations" (.Values.resource.all.metadata.annotations | default (dict))) | fromYaml -}} +{{- $user := (include "apply-non-reserved-gha-labels-and-annotations" $userRaw) | fromYaml -}} +{{- $annotations := mergeOverwrite $global $user -}} +{{- if not (empty $annotations) -}} + {{- toYaml $annotations -}} +{{- end -}} +{{- end }} + + diff --git a/charts/gha-runner-scale-set-dev/templates/_mode_dind.tpl b/charts/gha-runner-scale-set-dev/templates/_mode_dind.tpl new file mode 100644 index 00000000..e9cbed5e --- /dev/null +++ b/charts/gha-runner-scale-set-dev/templates/_mode_dind.tpl @@ -0,0 +1,121 @@ +{{- define "runner-mode-dind.runner-container" -}} +name: runner +image: {{ include "runner.image" . | quote }} +command: {{ include "runner.command" . }} +env: + - {{ include "runner-mode-dind.env-docker-host" . | nindent 4 }} + - {{ include "runner-mode-dind.env-wait-for-docker-timeout" . | nindent 4 }} + {{/* TODO:: Should we skip DOCKER_HOST and RUNNER_WAIT_FOR_DOCKER_IN_SECONDS? */}} + {{- with .Values.runner.env }} + {{- toYaml . | nindent 2 }} + {{- end }} +volumeMounts: + - name: work + mountPath: /home/runner/_work + - name: dind-sock + mountPath: {{ include "runner-mode-dind.sock-mount-dir" . | quote }} +{{- end }} + +{{- define "runner-mode-dind.dind-container" -}} +{{- $dind := .Values.runner.dind | default dict -}} +name: {{ $dind.container.name | default "dind" }} +image: {{ $dind.container.image | default "docker:dind" | quote }} +args: + {{- include "runner-mode-dind.args" . | nindent 2 }} +env: + - name: DOCKER_GROUP_GID + value: {{ ($dind.dockerGroupId | default "123") | quote }} +securityContext: +{{- if $dind.container.securityContext }} + {{- toYaml $dind.container.securityContext | nindent 2 }} +{{ else }} + {{- toYaml (dict "privileged" true) | nindent 2 }} +{{- end }} +restartPolicy: Always +startupProbe: + {{- include "runner-mode-dind.startup-probe" . | nindent 2 }} +volumeMounts: + - name: work + mountPath: /home/runner/_work + - name: dind-sock + mountPath: {{ include "runner-mode-dind.sock-mount-dir" . | quote }} +{{- if $dind.copyExternals }} + - name: dind-externals + mountPath: /home/runner/externals +{{- end }} +{{- end }} + +{{- define "runner-mode-dind.pod-volumes" -}} +- name: work + emptyDir: {} +- name: dind-sock + emptyDir: {} +{{- if .Values.runner.dind.copyExternals }} +- name: dind-externals + emptyDir: {} +{{- end }} +{{- end }} + +{{- define "runner-mode-dind.copy-externals" -}} +name: init-dind-externals +image: ghcr.io/actions/actions-runner:latest +command: ["cp", "-r", "/home/runner/externals/.", "/home/runner/tmpDir/"] +volumeMounts: + - name: dind-externals + mountPath: /home/runner/tmpDir +{{- end }} + +{{- define "runner-mode-dind.startup-probe" -}} +exec: + command: + - docker + - info +initialDelaySeconds: 0 +failureThreshold: 24 +periodSeconds: 5 +{{- end }} + +{{- define "runner-mode-dind.args" -}} +{{- $dind := .Values.runner.dind | default dict -}} +{{- $dockerSock := $dind.dockerSock | default "unix:///var/run/docker.sock" -}} +{{- if not (kindIs "string" $dockerSock) -}} + {{- fail "runner.dind.dockerSock must be a string" -}} +{{- end -}} +- dockerd +- {{ printf "--host=%s" $dockerSock }} +- --group=$(DOCKER_GROUP_GID) +{{- end }} + +{{- define "runner-mode-dind.env-docker-host" -}} +{{- $dind := .Values.runner.dind | default dict -}} +{{- $dockerSock := $dind.dockerSock | default "unix:///var/run/docker.sock" -}} +{{- if not (kindIs "string" $dockerSock) -}} + {{- fail "runner.dind.dockerSock must be a string" -}} +{{- end -}} +name: DOCKER_HOST +value: {{ $dockerSock | quote }} +{{- end }} + +{{- define "runner-mode-dind.env-wait-for-docker-timeout" -}} +{{- $dind := .Values.runner.dind | default dict -}} +{{- $waitForDockerInSeconds := $dind.waitForDockerInSeconds | default 120 -}} +{{- if not (or (kindIs "int" $waitForDockerInSeconds) (kindIs "int64" $waitForDockerInSeconds) (kindIs "float64" $waitForDockerInSeconds)) -}} + {{- fail "runner.dind.waitForDockerInSeconds must be a number" -}} +{{- end -}} +{{- $waitForDockerInSecondsInt := ($waitForDockerInSeconds | int) -}} +{{- if lt $waitForDockerInSecondsInt 0 -}} + {{- fail "runner.dind.waitForDockerInSeconds must be non-negative" -}} +{{- end -}} +name: RUNNER_WAIT_FOR_DOCKER_IN_SECONDS +value: {{ $waitForDockerInSecondsInt | toString | quote }} +{{- end }} + +{{- define "runner-mode-dind.sock-mount-dir" -}} +{{- $dind := .Values.runner.dind | default dict -}} +{{- $dockerSock := $dind.dockerSock | default "unix:///var/run/docker.sock" -}} +{{- if not (kindIs "string" $dockerSock) -}} + {{- fail "runner.dind.dockerSock must be a string" -}} +{{- end -}} +{{- $dockerSockPath := trimPrefix "unix://" $dockerSock -}} +{{- dir $dockerSockPath -}} +{{- end }} \ No newline at end of file diff --git a/charts/gha-runner-scale-set-dev/templates/_mode_kubernetes.tpl b/charts/gha-runner-scale-set-dev/templates/_mode_kubernetes.tpl new file mode 100644 index 00000000..9662550f --- /dev/null +++ b/charts/gha-runner-scale-set-dev/templates/_mode_kubernetes.tpl @@ -0,0 +1,49 @@ +{{- define "runner-mode-kubernetes.runner-container" -}} +{{- $runner := (.Values.runner | default dict) -}} +{{- $kubeMode := (index $runner "kubernetesMode" | default dict) -}} +{{- $hookPath := (index $kubeMode "hookPath" | default "/home/runner/k8s/index.js") -}} +{{- if not (kindIs "string" $hookPath) -}} + {{- fail "runner.kubernetesMode.hookPath must be a string" -}} +{{- end -}} +{{- $requireJobContainer := true -}} +{{- if hasKey $kubeMode "requireJobContainer" -}} + {{- $requireJobContainer = (index $kubeMode "requireJobContainer") -}} +{{- end -}} +{{- if not (kindIs "bool" $requireJobContainer) -}} + {{- fail "runner.kubernetesMode.requireJobContainer must be a bool" -}} +{{- end -}} +name: runner +image: {{ include "runner.image" . | quote }} +command: {{ include "runner.command" . }} +env: + - name: ACTIONS_RUNNER_CONTAINER_HOOKS + value: {{ $hookPath | quote }} + - name: ACTIONS_RUNNER_POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: ACTIONS_RUNNER_REQUIRE_JOB_CONTAINER + value: {{ ternary "true" "false" $requireJobContainer | quote }} + {{- with .Values.runner.env }} + {{- toYaml . | nindent 2 }} + {{- end }} +volumeMounts: + - name: work + mountPath: /home/runner/_work +{{- end }} + +{{- define "runner-mode-kubernetes.pod-volumes" -}} +{{- $runner := (.Values.runner | default dict) -}} +{{- $kubeMode := (index $runner "kubernetesMode" | default dict) -}} +{{- $claim := (index $kubeMode "workVolumeClaim" | default dict) -}} +{{- if and (not (empty $claim)) (not (kindIs "map" $claim)) -}} + {{- fail "runner.kubernetesMode.workVolumeClaim must be a map/object" -}} +{{- end -}} +{{- $defaultClaim := dict "accessModes" (list "ReadWriteOnce") "storageClassName" "local-path" "resources" (dict "requests" (dict "storage" "1Gi")) -}} +{{- $claimSpec := mergeOverwrite $defaultClaim $claim -}} +- name: work + ephemeral: + volumeClaimTemplate: + spec: + {{- toYaml $claimSpec | nindent 8 }} +{{- end }} diff --git a/charts/gha-runner-scale-set-dev/templates/autoscalingrunnserset.yaml b/charts/gha-runner-scale-set-dev/templates/autoscalingrunnserset.yaml new file mode 100644 index 00000000..ca185036 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/templates/autoscalingrunnserset.yaml @@ -0,0 +1,219 @@ +{{- $runner := (.Values.runner | default dict) }} +{{- $runnerMode := (index $runner "mode" | default "") }} +{{- $kubeMode := (index $runner "kubernetesMode" | default dict) }} +{{- $dind := (index $runner "dind" | default dict) }} +{{- $kubeDefaults := (index $kubeMode "default" | default true) }} +{{- $kubeServiceAccountName := (index $kubeMode "serviceAccountName" | default "") }} +{{- $usesKubernetesSecrets := or (not .Values.secretResolution) (eq .Values.secretResolution.type "kubernetes") }} + +{{- $runnerPod := (index $runner "pod" | default dict) -}} +{{- if not (kindIs "map" $runnerPod) -}} + {{- fail ".Values.runner.pod must be an object" -}} +{{- end }} +{{- $runnerPodSpec := (index $runnerPod "spec" | default dict) -}} +{{- if not (kindIs "map" $runnerPodSpec) -}} + {{- fail ".Values.runner.pod.spec must be an object" -}} +{{- end }} + +{{- $extraContainers := (index $runnerPodSpec "containers" | default list) -}} +{{- if not (kindIs "slice" $extraContainers) -}} + {{- fail ".Values.runner.pod.spec.containers must be a list of container specifications" -}} +{{- end }} +{{- range $extraContainers -}} + {{- if not (kindIs "map" .) -}} + {{- fail ".Values.runner.pod.spec.containers must be a list of container specifications" -}} + {{- end }} + {{- $extraContainerName := (index . "name" | default "") -}} + {{- if empty $extraContainerName -}} + {{- fail ".Values.runner.pod.spec.containers[].name is required" -}} + {{- end }} + {{- if eq $extraContainerName "runner" -}} + {{- fail ".Values.runner.pod.spec.containers[].name must not be 'runner' (reserved)" -}} + {{- end }} +{{- end }} +{{- $extraInitContainers := (index $runnerPodSpec "initContainers" | default list) -}} +{{- if not (kindIs "slice" $extraInitContainers) -}} + {{- fail ".Values.runner.pod.spec.initContainers must be a list of container specifications" -}} +{{- end }} +{{- range $extraInitContainers -}} + {{- if not (kindIs "map" .) -}} + {{- fail ".Values.runner.pod.spec.initContainers must be a list of container specifications" -}} + {{- end }} + {{- $extraInitContainerName := (index . "name" | default "") -}} + {{- if empty $extraInitContainerName -}} + {{- fail ".Values.runner.pod.spec.initContainers[].name is required" -}} + {{- end }} +{{- end }} + +{{- $runnerPodSpecExtraFields := (omit $runnerPodSpec "containers" "initContainers" "volumes" "serviceAccountName") -}} +{{- $extraVolumes := (index $runnerPodSpec "volumes" | default list) -}} +{{- if not (kindIs "slice" $extraVolumes) -}} + {{- fail ".Values.runner.pod.spec.volumes must be a list of volume specifications" -}} +{{- end }} +{{- $hasInitContainers := or (gt (len $extraInitContainers) 0) (eq $runnerMode "dind") -}} +{{- $hasVolumes := or (gt (len $extraVolumes) 0) (eq $runnerMode "kubernetes") (eq $runnerMode "dind") -}} +apiVersion: actions.github.com/v1alpha1 +kind: AutoscalingRunnerSet +metadata: + name: {{ include "autoscaling-runner-set.name" . | quote }} + namespace: {{ include "autoscaling-runner-set.namespace" . | quote }} + labels: + {{- include "autoscaling-runner-set.labels" . | nindent 4 }} + annotations: + {{- include "autoscaling-runner-set.annotations" . | nindent 4 }} + actions.github.com/values-hash: {{ toJson .Values | sha256sum | trunc 63 }} + {{- if and $usesKubernetesSecrets (empty .Values.auth.secretName) }} + actions.github.com/cleanup-github-secret-name: {{ include "github-secret.name" . | quote }} + {{- end }} + actions.github.com/cleanup-manager-role-binding: {{ include "manager-role-binding.name" . | quote }} + actions.github.com/cleanup-manager-role-name: {{ include "manager-role.name" . | quote }} + {{- if ne $runnerMode "kubernetes" }} + actions.github.com/cleanup-no-permission-service-account-name: {{ include "no-permission-serviceaccount.name" . | quote }} + {{- end }} + {{- if and (eq $runnerMode "kubernetes") $kubeDefaults (empty $kubeServiceAccountName) }} + actions.github.com/cleanup-kubernetes-mode-role-binding-name: {{ include "kube-mode-role-binding.name" . | quote }} + actions.github.com/cleanup-kubernetes-mode-role-name: {{ include "kube-mode-role.name" . | quote }} + actions.github.com/cleanup-kubernetes-mode-service-account-name: {{ include "kube-mode-serviceaccount.name" . | quote }} + {{- end }} + +spec: + githubConfigUrl: {{ required ".Values.auth.url is required" (trimSuffix "/" .Values.auth.url) | quote }} + githubConfigSecret: {{ include "github-secret.name" . | quote }} + runnerGroup: {{ .Values.scaleset.runnerGroup | quote }} + runnerScaleSetName: {{ .Values.scaleset.name | quote }} + + {{- if .Values.githubServerTLS }} + githubServerTLS: + {{- with .Values.githubServerTLS.certificateFrom }} + certificateFrom: + configMapKeyRef: + name: {{ .configMapKeyRef.name }} + key: {{ .configMapKeyRef.key }} + {{- end }} + {{- end }} + + {{- if and .Values.secretResolution (ne .Values.secretResolution.type "kubernetes") }} + vaultConfig: + type: {{ .Values.secretResolution.type }} + {{- if .Values.secretResolution.proxy }} + proxy: {{- toYaml .Values.secretResolution.proxy | nindent 6 }} + {{- end }} + {{- if eq .Values.secretResolution.type "azureKeyVault" }} + azureKeyVault: + url: {{ .Values.secretResolution.azureKeyVault.url }} + tenantId: {{ .Values.secretResolution.azureKeyVault.tenantId }} + clientId: {{ .Values.secretResolution.azureKeyVault.clientId }} + certificatePath: {{ .Values.secretResolution.azureKeyVault.certificatePath }} + secretKey: {{ .Values.secretResolution.azureKeyVault.secretKey }} + {{- else }} + {{- fail (printf "Unsupported keyVault type: %s" .Values.secretResolution.type) }} + {{- end }} + {{- end }} + + {{- if .Values.proxy }} + proxy: + {{- if .Values.proxy.http }} + http: + url: {{ .Values.proxy.http.url }} + {{- if .Values.proxy.http.credentialSecretRef }} + credentialSecretRef: {{ .Values.proxy.http.credentialSecretRef }} + {{- end }} + {{- end }} + {{- if .Values.proxy.https }} + https: + url: {{ .Values.proxy.https.url }} + {{- if .Values.proxy.https.credentialSecretRef }} + credentialSecretRef: {{ .Values.proxy.https.credentialSecretRef }} + {{- end }} + {{- end }} + {{- if and .Values.proxy.noProxy (kindIs "slice" .Values.proxy.noProxy) }} + noProxy: {{ .Values.proxy.noProxy | toYaml | nindent 6}} + {{- end }} + {{- end }} + + {{- if and (or (kindIs "int64" .Values.scaleset.minRunners) (kindIs "float64" .Values.scaleset.minRunners)) (or (kindIs "int64" .Values.scaleset.maxRunners) (kindIs "float64" .Values.scaleset.maxRunners)) }} + {{- if gt .Values.scaleset.minRunners .Values.scaleset.maxRunners }} + {{- fail "maxRunners has to be greater or equal to minRunners" }} + {{- end }} + {{- end }} + + {{- if or (kindIs "int64" .Values.scaleset.maxRunners) (kindIs "float64" .Values.scaleset.maxRunners)}} + {{- if lt (.Values.scaleset.maxRunners | int) 0 }} + {{- fail "maxRunners has to be greater or equal to 0" }} + {{- end }} + maxRunners: {{ .Values.scaleset.maxRunners | int }} + {{- end }} + + {{- if or (kindIs "int64" .Values.scaleset.minRunners) (kindIs "float64" .Values.scaleset.minRunners) }} + {{- if lt (.Values.scaleset.minRunners | int) 0 }} + {{- fail "minRunners has to be greater or equal to 0" }} + {{- end }} + minRunners: {{ .Values.scaleset.minRunners | int }} + {{- end }} + + {{- with .Values.listenerPodTemplate }} + listenerTemplate: + {{- toYaml . | nindent 4}} + {{- end }} + + {{- with .Values.listenerMetrics }} + listenerMetrics: + {{- toYaml . | nindent 4 }} + {{- end }} + + template: + {{- $runnerPodLabels := (include "autoscaling-runner-set.runner-pod.labels" .) -}} + {{- $runnerPodAnnotations := (include "autoscaling-runner-set.runner-pod.annotations" .) -}} + {{- if or $runnerPodLabels $runnerPodAnnotations }} + metadata: + {{- if $runnerPodLabels }} + labels: + {{- $runnerPodLabels | nindent 8 }} + {{- end }} + {{- if $runnerPodAnnotations }} + annotations: + {{- $runnerPodAnnotations | nindent 8 }} + {{- end }} + {{- end }} + spec: + serviceAccountName: {{ include "autoscaling-runner-set.template-service-account" . | quote }} + {{- if gt (len $runnerPodSpecExtraFields) 0 }} + {{- toYaml $runnerPodSpecExtraFields | nindent 6 }} + {{- end }} + {{- if $hasInitContainers }} + initContainers: + {{- if and (eq $runnerMode "dind") $dind.copyExternals }} + - {{ include "runner-mode-dind.copy-externals" . | nindent 10 }} + {{- end }} + {{- range $extraInitContainers }} + - {{ toYaml . | nindent 10 }} + {{- end }} + {{- if eq $runnerMode "dind" }} + - {{ include "runner-mode-dind.dind-container" . | nindent 10 }} + {{- end }} + {{- end }} + containers: + - + {{- if eq $runnerMode "kubernetes" }} + {{- include "runner-mode-kubernetes.runner-container" . | nindent 10 }} + {{- else }} + {{- include "runner-mode-dind.runner-container" . | nindent 10 }} + {{- end }} + {{- if $extraContainers }} + {{- range $extraContainers }} + - {{ toYaml . | nindent 10 }} + {{- end }} + {{- end }} + {{- if $hasVolumes }} + volumes: + {{- if eq $runnerMode "kubernetes" }} + {{- include "runner-mode-kubernetes.pod-volumes" . | nindent 8 }} + {{- else }} + {{- include "runner-mode-dind.pod-volumes" . | nindent 8 }} + {{- end }} + {{- if $extraVolumes }} + {{- range $extraVolumes }} + - {{ toYaml . | nindent 10 }} + {{- end }} + {{- end }} + {{- end }} \ No newline at end of file diff --git a/charts/gha-runner-scale-set-dev/templates/githubsecret.yaml b/charts/gha-runner-scale-set-dev/templates/githubsecret.yaml new file mode 100644 index 00000000..0630891e --- /dev/null +++ b/charts/gha-runner-scale-set-dev/templates/githubsecret.yaml @@ -0,0 +1,28 @@ +{{- $usesKubernetesSecrets := or (not .Values.secretResolution) (eq .Values.secretResolution.type "kubernetes") -}} + +{{- if and (not $usesKubernetesSecrets) (empty .Values.auth.secretName) -}} + {{- fail ".Values.auth.secretName is required when .Values.secretResolution.type is not \"kubernetes\"" -}} +{{- end -}} + +{{- if and $usesKubernetesSecrets (empty .Values.auth.secretName) -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "github-secret.name" . | quote }} + namespace: {{ include "autoscaling-runner-set.namespace" . | quote }} + labels: + {{- include "github-secret.labels" . | nindent 4 }} + annotations: + {{- include "github-secret.annotations" . | nindent 4 }} + finalizers: + - actions.github.com/cleanup-protection +type: Opaque +data: + {{- if not (empty .Values.auth.app.clientId) }} + github_app_id: {{ .Values.auth.app.clientId | toString | b64enc }} + github_app_installation_id: {{ required ".Values.auth.app.installationId is required when using GitHub App auth" .Values.auth.app.installationId | toString | b64enc }} + github_app_private_key: {{ required ".Values.auth.app.privateKey is required when using GitHub App auth" .Values.auth.app.privateKey | toString | b64enc }} + {{- else }} + github_token: {{ required ".Values.auth.githubToken is required when auth.secretName and auth.app.clientId are not set" .Values.auth.githubToken | toString | b64enc }} + {{- end }} +{{- end -}} diff --git a/charts/gha-runner-scale-set-dev/templates/kube_mode_role.yaml b/charts/gha-runner-scale-set-dev/templates/kube_mode_role.yaml new file mode 100644 index 00000000..acaef34f --- /dev/null +++ b/charts/gha-runner-scale-set-dev/templates/kube_mode_role.yaml @@ -0,0 +1,42 @@ +{{- $runner := (.Values.runner | default dict) -}} +{{- $runnerMode := (index $runner "mode" | default "") -}} +{{- $kubeMode := (index $runner "kubernetesMode" | default dict) -}} +{{- $kubeDefaults := (index $kubeMode "default" | default true) -}} +{{- $kubeServiceAccountName := (index $kubeMode "serviceAccountName" | default "") -}} +{{- if and (eq $runnerMode "kubernetes") $kubeDefaults (empty $kubeServiceAccountName) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "kube-mode-role.name" . | quote }} + namespace: {{ include "autoscaling-runner-set.namespace" . | quote }} + labels: + {{- include "kube-mode-role.labels" . | nindent 4 }} + annotations: + {{- include "kube-mode-role.annotations" . | nindent 4 }} + finalizers: + - actions.github.com/cleanup-protection +rules: + - apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "create", "delete"] + - apiGroups: [""] + resources: ["pods/exec"] + verbs: ["get", "create"] + - apiGroups: [""] + resources: ["pods/log"] + verbs: ["get", "list", "watch"] + - apiGroups: ["batch"] + resources: ["jobs"] + verbs: ["get", "list", "create", "delete"] + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list", "create", "delete"] + {{- with .Values.resource.kubernetesModeRole.extraRules }} + {{- if not (empty .) }} + {{- if not (kindIs "slice" .) -}} + {{- fail ".Values.resource.kubernetesModeRole.extraRules must be a list of RBAC policy rules" -}} + {{- end }} +{{ toYaml . | nindent 2 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/gha-runner-scale-set-dev/templates/kube_mode_role_binding.yaml b/charts/gha-runner-scale-set-dev/templates/kube_mode_role_binding.yaml new file mode 100644 index 00000000..c2ecc565 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/templates/kube_mode_role_binding.yaml @@ -0,0 +1,26 @@ +{{- $runner := (.Values.runner | default dict) -}} +{{- $runnerMode := (index $runner "mode" | default "") -}} +{{- $kubeMode := (index $runner "kubernetesMode" | default dict) -}} +{{- $kubeDefaults := (index $kubeMode "default" | default true) -}} +{{- $kubeServiceAccountName := (index $kubeMode "serviceAccountName" | default "") -}} +{{- if and (eq $runnerMode "kubernetes") $kubeDefaults (empty $kubeServiceAccountName) }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "kube-mode-role-binding.name" . | quote }} + namespace: {{ include "autoscaling-runner-set.namespace" . | quote }} + labels: + {{- include "kube-mode-role-binding.labels" . | nindent 4 }} + annotations: + {{- include "kube-mode-role-binding.annotations" . | nindent 4 }} + finalizers: + - actions.github.com/cleanup-protection +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "kube-mode-role.name" . | quote }} +subjects: + - kind: ServiceAccount + name: {{ include "kube-mode-serviceaccount.name" . | quote }} + namespace: {{ include "autoscaling-runner-set.namespace" . | quote }} +{{- end }} diff --git a/charts/gha-runner-scale-set-dev/templates/kube_mode_serviceaccount.yaml b/charts/gha-runner-scale-set-dev/templates/kube_mode_serviceaccount.yaml new file mode 100644 index 00000000..0e8b0b7a --- /dev/null +++ b/charts/gha-runner-scale-set-dev/templates/kube_mode_serviceaccount.yaml @@ -0,0 +1,18 @@ +{{- $runner := (.Values.runner | default dict) -}} +{{- $runnerMode := (index $runner "mode" | default "") -}} +{{- $kubeMode := (index $runner "kubernetesMode" | default dict) -}} +{{- $kubeDefaults := (index $kubeMode "default" | default true) -}} +{{- $kubeServiceAccountName := (index $kubeMode "serviceAccountName" | default "") -}} +{{- if and (eq $runnerMode "kubernetes") $kubeDefaults (empty $kubeServiceAccountName) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "kube-mode-serviceaccount.name" . | quote }} + namespace: {{ include "autoscaling-runner-set.namespace" . | quote }} + labels: + {{- include "kube-mode-serviceaccount.labels" . | nindent 4 }} + annotations: + {{- include "kube-mode-serviceaccount.annotations" . | nindent 4 }} + finalizers: + - actions.github.com/cleanup-protection +{{- end }} diff --git a/charts/gha-runner-scale-set-dev/templates/manager_role.yaml b/charts/gha-runner-scale-set-dev/templates/manager_role.yaml new file mode 100644 index 00000000..2990ccc4 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/templates/manager_role.yaml @@ -0,0 +1,85 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "manager-role.name" . | quote }} + namespace: {{ include "autoscaling-runner-set.namespace" . | quote }} + labels: + {{- include "manager-role.labels" . | nindent 4 }} + annotations: + {{- include "manager-role.annotations" . | nindent 4 }} + finalizers: + - actions.github.com/cleanup-protection +rules: +- apiGroups: + - "" + resources: + - pods + verbs: + - create + - delete + - get +- apiGroups: + - "" + resources: + - pods/status + verbs: + - get +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update +- apiGroups: + - "" + resources: + - serviceaccounts + verbs: + - create + - delete + - get + - list + - patch + - update +- apiGroups: + - rbac.authorization.k8s.io + resources: + - rolebindings + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + verbs: + - create + - delete + - get + - patch + - update +{{- if .Values.githubServerTLS }} +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get +{{- end }} + +{{- with .Values.resource.managerRole.extraRules }} + {{- if not (empty .) }} + {{- if not (kindIs "slice" .) -}} + {{- fail ".Values.resource.managerRole.extraRules must be a list of RBAC policy rules" -}} + {{- end }} +{{ toYaml . }} + {{- end }} +{{- end }} diff --git a/charts/gha-runner-scale-set-dev/templates/manager_role_binding.yaml b/charts/gha-runner-scale-set-dev/templates/manager_role_binding.yaml new file mode 100644 index 00000000..ae5e67cf --- /dev/null +++ b/charts/gha-runner-scale-set-dev/templates/manager_role_binding.yaml @@ -0,0 +1,22 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "manager-role-binding.name" . | quote }} + namespace: {{ include "autoscaling-runner-set.namespace" . | quote }} + labels: + {{- include "manager-role-binding.labels" . | nindent 4 }} + annotations: + {{- include "manager-role-binding.annotations" . | nindent 4 }} + finalizers: + - actions.github.com/cleanup-protection +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "manager-role.name" . | quote }} +subjects: + {{- if not .Values.controllerServiceAccount }} + {{- fail "controllerServiceAccount must be set" -}} + {{- end }} + - kind: ServiceAccount + name: {{ required .Values.controllerServiceAccount.name "controllerServiceAccount.name must be set" | quote }} + namespace: {{ required .Values.controllerServiceAccount.namespace "controllerServiceAccount.namespace must be set" | quote }} diff --git a/charts/gha-runner-scale-set-dev/templates/no_permission_serviceaccount.yaml b/charts/gha-runner-scale-set-dev/templates/no_permission_serviceaccount.yaml new file mode 100644 index 00000000..6a2d53a4 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/templates/no_permission_serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- $runnerMode := (.Values.runner.mode | default "") -}} +{{- if ne $runnerMode "kubernetes" -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "no-permission-serviceaccount.name" . | quote }} + namespace: {{ include "autoscaling-runner-set.namespace" . | quote }} + labels: + {{- include "no-permission-serviceaccount.labels" . | nindent 4 }} + annotations: + {{- include "no-permission-serviceaccount.annotations" . | nindent 4 }} + finalizers: + - actions.github.com/cleanup-protection +{{- end }} diff --git a/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_annotations_test.yaml b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_annotations_test.yaml new file mode 100644 index 00000000..9021e6b0 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_annotations_test.yaml @@ -0,0 +1,79 @@ +suite: "Test AutoscalingRunnerSet Annotations" +templates: + - autoscalingrunnserset.yaml +tests: + - it: should render values-hash annotation + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - exists: + path: metadata.annotations["actions.github.com/values-hash"] + + - it: should merge global and resource annotations (resource overrides global) + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + resource: + all: + metadata: + annotations: + a: "global" + shared: "global" + autoscalingRunnerSet: + metadata: + annotations: + b: "resource" + shared: "resource" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: metadata.annotations.a + value: "global" + - equal: + path: metadata.annotations.b + value: "resource" + - equal: + path: metadata.annotations.shared + value: "resource" + + - it: should not allow overriding reserved values-hash annotation + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + resource: + all: + metadata: + annotations: + actions.github.com/values-hash: "user-value" + ok: "ok" + autoscalingRunnerSet: + metadata: + annotations: + actions.github.com/cleanup-something: "should-not-render" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: metadata.annotations.ok + value: "ok" + - notEqual: + path: metadata.annotations["actions.github.com/values-hash"] + value: "user-value" + - notExists: + path: metadata.annotations["actions.github.com/cleanup-something"] diff --git a/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_auth_test.yaml b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_auth_test.yaml new file mode 100644 index 00000000..a0f6fbac --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_auth_test.yaml @@ -0,0 +1,245 @@ +suite: "Test AutoscalingRunnerSet Authentication & Configuration" +templates: + - autoscalingrunnserset.yaml +tests: + - it: should require githubConfigUrl + set: + scaleset.name: "test" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - failedTemplate: + errorMessage: ".Values.auth.url is required" + + - it: should render githubConfigUrl from auth.url + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.githubConfigUrl + value: "https://github.com/org" + + - it: should trim trailing slash from githubConfigUrl + set: + scaleset.name: "test" + auth.url: "https://github.com/org/" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.githubConfigUrl + value: "https://github.com/org" + + - it: should render default githubConfigSecret from release name + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.githubConfigSecret + value: "test-name-github-secret" + + - it: should render custom githubConfigSecret when auth.secretName is provided + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + auth.secretName: "custom-github-secret" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.githubConfigSecret + value: "custom-github-secret" + + - it: should render default runnerGroup when not configured + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.runnerGroup + value: "default" + + - it: should render custom runnerGroup when configured + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + scaleset.runnerGroup: "custom-group" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.runnerGroup + value: "custom-group" + + - it: should render runnerGroup with special characters + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + scaleset.runnerGroup: "my-custom-runner-group-123" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.runnerGroup + value: "my-custom-runner-group-123" + + - it: should render runnerScaleSetName from scaleset.name + set: + scaleset.name: "my-runner-set" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.runnerScaleSetName + value: "my-runner-set" + + - it: should use release name as metadata name when runnerScaleSetName not provided + set: + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "release-name" + namespace: "test-namespace" + asserts: + - equal: + path: metadata.name + value: "release-name" + + - it: should use scaleset.name for spec.runnerScaleSetName when provided + set: + scaleset.name: "spec-runner-name" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "release-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.runnerScaleSetName + value: "spec-runner-name" + + - it: should not normalize underscores in runnerScaleSetName (underscores are preserved) + set: + scaleset.name: "my_runner_set" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.runnerScaleSetName + value: "my_runner_set" + + - it: should reject metadata name exceeding 45 characters + set: + runnerScaleSetName: "this-is-a-very-long-name-that-exceeds-forty-five-characters-long" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - failedTemplate: + errorMessage: "Autoscaling runner set name must have up to 45 characters" + + - it: should handle githubConfigUrl with enterprise GitHub instance + set: + scaleset.name: "test" + auth.url: "https://github.enterprise.com/api/v3" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.githubConfigUrl + value: "https://github.enterprise.com/api/v3" + + - it: should render all configuration together + set: + runnerScaleSetName: "prod-runners" + scaleset.name: "prod-spec-name" + auth.url: "https://github.com/myorg" + auth.githubToken: "gh_token12345" + auth.secretName: "gh-token-secret" + scaleset.runnerGroup: "prod-group" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "prod-scale-set" + namespace: "arc" + asserts: + - equal: + path: spec.githubConfigUrl + value: "https://github.com/myorg" + - equal: + path: spec.githubConfigSecret + value: "gh-token-secret" + - equal: + path: spec.runnerGroup + value: "prod-group" + - equal: + path: spec.runnerScaleSetName + value: "prod-spec-name" + - equal: + path: metadata.name + value: "prod-runners" + - equal: + path: metadata.namespace + value: "arc" diff --git a/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_dind_mode_spec_test.yaml b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_dind_mode_spec_test.yaml new file mode 100644 index 00000000..383ab1f8 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_dind_mode_spec_test.yaml @@ -0,0 +1,342 @@ +suite: "AutoscalingRunnerSet dind mode podspec" +templates: + - autoscalingrunnserset.yaml +tests: + - it: should render the default dind pod spec (initContainers, runner container, volumes) + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + mode: "dind" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.template.spec.initContainers[0].name + value: init-dind-externals + - equal: + path: spec.template.spec.initContainers[0].image + value: ghcr.io/actions/actions-runner:latest + - equal: + path: spec.template.spec.initContainers[0].command[0] + value: cp + - equal: + path: spec.template.spec.initContainers[1].name + value: dind + - equal: + path: spec.template.spec.initContainers[1].image + value: docker:dind + - equal: + path: spec.template.spec.initContainers[1].args[0] + value: dockerd + - equal: + path: spec.template.spec.initContainers[1].args[1] + value: --host=unix:///var/run/docker.sock + - equal: + path: spec.template.spec.initContainers[1].args[2] + value: --group=$(DOCKER_GROUP_GID) + - equal: + path: spec.template.spec.initContainers[1].env[0].name + value: DOCKER_GROUP_GID + - equal: + path: spec.template.spec.initContainers[1].env[0].value + value: "123" + - equal: + path: spec.template.spec.initContainers[1].securityContext.privileged + value: true + - equal: + path: spec.template.spec.initContainers[1].startupProbe.exec.command[0] + value: docker + - equal: + path: spec.template.spec.initContainers[1].startupProbe.exec.command[1] + value: info + - equal: + path: spec.template.spec.initContainers[1].volumeMounts[0].name + value: work + - equal: + path: spec.template.spec.initContainers[1].volumeMounts[0].mountPath + value: /home/runner/_work + - equal: + path: spec.template.spec.initContainers[1].volumeMounts[1].name + value: dind-sock + - equal: + path: spec.template.spec.initContainers[1].volumeMounts[1].mountPath + value: /var/run + + - equal: + path: spec.template.spec.containers[0].name + value: runner + - equal: + path: spec.template.spec.containers[0].image + value: ghcr.io/actions/actions-runner:latest + - equal: + path: spec.template.spec.containers[0].env[0].name + value: DOCKER_HOST + - equal: + path: spec.template.spec.containers[0].env[0].value + value: unix:///var/run/docker.sock + - equal: + path: spec.template.spec.containers[0].env[1].name + value: RUNNER_WAIT_FOR_DOCKER_IN_SECONDS + - equal: + path: spec.template.spec.containers[0].env[1].value + value: "120" + - equal: + path: spec.template.spec.containers[0].volumeMounts[0].name + value: work + - equal: + path: spec.template.spec.containers[0].volumeMounts[0].mountPath + value: /home/runner/_work + - equal: + path: spec.template.spec.containers[0].volumeMounts[1].name + value: dind-sock + - equal: + path: spec.template.spec.containers[0].volumeMounts[1].mountPath + value: /var/run + + - contains: + path: spec.template.spec.volumes + content: + name: work + emptyDir: {} + - contains: + path: spec.template.spec.volumes + content: + name: dind-sock + emptyDir: {} + - contains: + path: spec.template.spec.volumes + content: + name: dind-externals + emptyDir: {} + + - it: should omit init-dind-externals and dind-externals volume when copyExternals is false + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + mode: "dind" + dind: + copyExternals: false + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.template.spec.initContainers[0].name + value: dind + - notExists: + path: spec.template.spec.initContainers[1] + - notExists: + path: spec.template.spec.volumes[2] + - notExists: + path: spec.template.spec.initContainers[0].volumeMounts[2] + + - it: should allow overriding dind container name, image, and securityContext + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + mode: "dind" + dind: + container: + name: "dockerd-custom" + image: "docker:27.3-dind" + securityContext: + privileged: false + runAsUser: 1000 + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.template.spec.initContainers[1].name + value: dockerd-custom + - equal: + path: spec.template.spec.initContainers[1].image + value: docker:27.3-dind + - equal: + path: spec.template.spec.initContainers[1].securityContext.privileged + value: false + - equal: + path: spec.template.spec.initContainers[1].securityContext.runAsUser + value: 1000 + + - it: should respect dockerSock override in DOCKER_HOST, mountPath, and dind args + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + mode: "dind" + dind: + dockerSock: "unix:///var/run/custom/docker.sock" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.template.spec.containers[0].env[0].name + value: DOCKER_HOST + - equal: + path: spec.template.spec.containers[0].env[0].value + value: unix:///var/run/custom/docker.sock + - equal: + path: spec.template.spec.containers[0].volumeMounts[1].mountPath + value: /var/run/custom + - equal: + path: spec.template.spec.initContainers[1].volumeMounts[1].mountPath + value: /var/run/custom + - equal: + path: spec.template.spec.initContainers[1].args[1] + value: --host=unix:///var/run/custom/docker.sock + + - it: should respect waitForDockerInSeconds override + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + mode: "dind" + dind: + waitForDockerInSeconds: 30 + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.template.spec.containers[0].env[1].name + value: RUNNER_WAIT_FOR_DOCKER_IN_SECONDS + - equal: + path: spec.template.spec.containers[0].env[1].value + value: "30" + + - it: should include extraInitContainers in dind mode + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + mode: "dind" + pod: + spec: + initContainers: + - name: "extra-init-1" + image: "busybox:1.36" + imagePullPolicy: IfNotPresent + command: + - "sh" + - "-c" + args: + - "echo extra-init && env | grep FOO" + env: + - name: FOO + value: BAR + resources: + requests: + cpu: 10m + memory: 16Mi + limits: + cpu: 100m + memory: 64Mi + securityContext: + runAsNonRoot: true + runAsUser: 1000 + volumeMounts: + - name: work + mountPath: /work + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.template.spec.initContainers[0].name + value: init-dind-externals + - equal: + path: spec.template.spec.initContainers[1].name + value: extra-init-1 + - equal: + path: spec.template.spec.initContainers[1].image + value: busybox:1.36 + - equal: + path: spec.template.spec.initContainers[1].imagePullPolicy + value: IfNotPresent + - equal: + path: spec.template.spec.initContainers[1].command[0] + value: sh + - equal: + path: spec.template.spec.initContainers[1].args[0] + value: echo extra-init && env | grep FOO + - equal: + path: spec.template.spec.initContainers[1].env[0].name + value: FOO + - equal: + path: spec.template.spec.initContainers[1].env[0].value + value: BAR + - equal: + path: spec.template.spec.initContainers[1].resources.requests.cpu + value: 10m + - equal: + path: spec.template.spec.initContainers[1].resources.requests.memory + value: 16Mi + - equal: + path: spec.template.spec.initContainers[1].resources.limits.cpu + value: 100m + - equal: + path: spec.template.spec.initContainers[1].resources.limits.memory + value: 64Mi + - equal: + path: spec.template.spec.initContainers[1].securityContext.runAsNonRoot + value: true + - equal: + path: spec.template.spec.initContainers[1].securityContext.runAsUser + value: 1000 + - equal: + path: spec.template.spec.initContainers[1].volumeMounts[0].name + value: work + - equal: + path: spec.template.spec.initContainers[1].volumeMounts[0].mountPath + value: /work + - equal: + path: spec.template.spec.initContainers[2].name + value: dind + + - it: should include extraVolumes in dind mode + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + mode: "dind" + pod: + spec: + volumes: + - name: "cache" + emptyDir: {} + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - contains: + path: spec.template.spec.volumes + content: + name: cache + emptyDir: {} diff --git a/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_extra_containers_test.yaml b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_extra_containers_test.yaml new file mode 100644 index 00000000..4ce639f1 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_extra_containers_test.yaml @@ -0,0 +1,84 @@ +suite: "AutoscalingRunnerSet extraContainers" +templates: + - autoscalingrunnserset.yaml +tests: + - it: should append extraContainers after the runner container + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + mode: "kubernetes" + pod: + spec: + containers: + - name: "sidecar" + image: "busybox:1.36" + command: ["sh", "-c", "sleep 3600"] + resources: + limits: + cpu: "250m" + memory: "64Mi" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.template.spec.containers[0].name + value: runner + - equal: + path: spec.template.spec.containers[1].name + value: sidecar + - equal: + path: spec.template.spec.containers[1].image + value: busybox:1.36 + - equal: + path: spec.template.spec.containers[1].resources.limits.cpu + value: 250m + - equal: + path: spec.template.spec.containers[1].resources.limits.memory + value: 64Mi + + - it: should fail when runner.pod.spec.containers is not a list + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + mode: "kubernetes" + pod: + spec: + containers: + name: "not-a-list" + image: "busybox:1.36" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - failedTemplate: + errorMessage: ".Values.runner.pod.spec.containers must be a list of container specifications" + + - it: should fail when a container is named runner + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + mode: "kubernetes" + pod: + spec: + containers: + - name: "runner" + image: "busybox:1.36" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - failedTemplate: + errorMessage: ".Values.runner.pod.spec.containers[].name must not be 'runner' (reserved)" diff --git a/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_extra_init_containers_test.yaml b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_extra_init_containers_test.yaml new file mode 100644 index 00000000..7dc0c90b --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_extra_init_containers_test.yaml @@ -0,0 +1,84 @@ +suite: "AutoscalingRunnerSet initContainers" +templates: + - autoscalingrunnserset.yaml +tests: + - it: should render initContainers when runner.pod.spec.initContainers configured in non-dind mode + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + pod: + spec: + initContainers: + - name: "extra-init-1" + image: "busybox:1.36" + imagePullPolicy: IfNotPresent + command: + - "sh" + - "-c" + args: + - "echo non-dind extra init" + env: + - name: FOO + value: BAR + securityContext: + runAsNonRoot: true + runAsUser: 1000 + resources: + requests: + cpu: 10m + memory: 16Mi + limits: + cpu: 100m + memory: 64Mi + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.template.spec.initContainers[0].name + value: extra-init-1 + - equal: + path: spec.template.spec.initContainers[0].image + value: busybox:1.36 + - equal: + path: spec.template.spec.initContainers[0].imagePullPolicy + value: IfNotPresent + - equal: + path: spec.template.spec.initContainers[0].command[0] + value: sh + - equal: + path: spec.template.spec.initContainers[0].args[0] + value: echo non-dind extra init + - equal: + path: spec.template.spec.initContainers[0].env[0].name + value: FOO + - equal: + path: spec.template.spec.initContainers[0].env[0].value + value: BAR + - equal: + path: spec.template.spec.initContainers[0].securityContext.runAsNonRoot + value: true + - equal: + path: spec.template.spec.initContainers[0].securityContext.runAsUser + value: 1000 + - equal: + path: spec.template.spec.initContainers[0].resources.requests.cpu + value: 10m + - equal: + path: spec.template.spec.initContainers[0].resources.requests.memory + value: 16Mi + - equal: + path: spec.template.spec.initContainers[0].resources.limits.cpu + value: 100m + - equal: + path: spec.template.spec.initContainers[0].resources.limits.memory + value: 64Mi + - notExists: + path: spec.template.spec.initContainers[1] + - equal: + path: spec.template.spec.containers[0].name + value: runner diff --git a/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_github_server_tls_test.yaml b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_github_server_tls_test.yaml new file mode 100644 index 00000000..9953dad7 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_github_server_tls_test.yaml @@ -0,0 +1,61 @@ +suite: "GitHub Server TLS" +templates: + - autoscalingrunnserset.yaml + - manager_role.yaml +tests: + - it: should render spec.githubServerTLS and allow manager role to read the configmap + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + githubServerTLS: + certificateFrom: + configMapKeyRef: + name: "my-ca-config" + key: "ca.crt" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.githubServerTLS.certificateFrom.configMapKeyRef.name + value: my-ca-config + template: autoscalingrunnserset.yaml + - equal: + path: spec.githubServerTLS.certificateFrom.configMapKeyRef.key + value: ca.crt + template: autoscalingrunnserset.yaml + - contains: + path: rules + content: + apiGroups: + - "" + resources: + - configmaps + verbs: + - get + template: manager_role.yaml + + - it: should not include the manager role configmap rule when githubServerTLS is not configured + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - notContains: + path: rules + content: + apiGroups: + - "" + resources: + - configmaps + verbs: + - get + template: manager_role.yaml diff --git a/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_kubernetes_mode_spec_test.yaml b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_kubernetes_mode_spec_test.yaml new file mode 100644 index 00000000..91429779 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_kubernetes_mode_spec_test.yaml @@ -0,0 +1,133 @@ +suite: "AutoscalingRunnerSet kubernetes mode podspec" +templates: + - autoscalingrunnserset.yaml +tests: + - it: should render the default kubernetes pod spec (runner container, ephemeral work volume) + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + mode: "kubernetes" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - notExists: + path: spec.template.spec.initContainers + - equal: + path: spec.template.spec.serviceAccountName + value: test-name-kube-mode + - equal: + path: spec.template.spec.containers[0].name + value: runner + - equal: + path: spec.template.spec.containers[0].image + value: ghcr.io/actions/actions-runner:latest + - equal: + path: spec.template.spec.containers[0].env[0].name + value: ACTIONS_RUNNER_CONTAINER_HOOKS + - equal: + path: spec.template.spec.containers[0].env[0].value + value: /home/runner/k8s/index.js + - equal: + path: spec.template.spec.containers[0].env[1].name + value: ACTIONS_RUNNER_POD_NAME + - equal: + path: spec.template.spec.containers[0].env[1].valueFrom.fieldRef.fieldPath + value: metadata.name + - equal: + path: spec.template.spec.containers[0].env[2].name + value: ACTIONS_RUNNER_REQUIRE_JOB_CONTAINER + - equal: + path: spec.template.spec.containers[0].env[2].value + value: "true" + - equal: + path: spec.template.spec.containers[0].volumeMounts[0].name + value: work + - equal: + path: spec.template.spec.containers[0].volumeMounts[0].mountPath + value: /home/runner/_work + - notExists: + path: spec.template.spec.containers[0].volumeMounts[1] + - equal: + path: spec.template.spec.volumes[0].name + value: work + - equal: + path: spec.template.spec.volumes[0].ephemeral.volumeClaimTemplate.spec.accessModes[0] + value: ReadWriteOnce + - equal: + path: spec.template.spec.volumes[0].ephemeral.volumeClaimTemplate.spec.storageClassName + value: local-path + - equal: + path: spec.template.spec.volumes[0].ephemeral.volumeClaimTemplate.spec.resources.requests.storage + value: 1Gi + + - it: should allow overriding kubernetes mode hookPath, requireJobContainer, and workVolumeClaim + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + mode: "kubernetes" + kubernetesMode: + hookPath: "/home/runner/custom/k8s/index.js" + requireJobContainer: false + workVolumeClaim: + storageClassName: "fast-ssd" + resources: + requests: + storage: 10Gi + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.template.spec.containers[0].env[0].value + value: /home/runner/custom/k8s/index.js + - equal: + path: spec.template.spec.containers[0].env[2].value + value: "false" + - equal: + path: spec.template.spec.volumes[0].ephemeral.volumeClaimTemplate.spec.storageClassName + value: fast-ssd + - equal: + path: spec.template.spec.volumes[0].ephemeral.volumeClaimTemplate.spec.resources.requests.storage + value: 10Gi + + - it: should include extraVolumes in kubernetes mode + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + mode: "kubernetes" + pod: + spec: + volumes: + - name: cache + emptyDir: {} + - name: custom-config + configMap: + name: example-config + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - contains: + path: spec.template.spec.volumes + content: + name: cache + emptyDir: {} + - contains: + path: spec.template.spec.volumes + content: + name: custom-config + configMap: + name: example-config diff --git a/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_labels_test.yaml b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_labels_test.yaml new file mode 100644 index 00000000..71cbabf5 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_labels_test.yaml @@ -0,0 +1,293 @@ +suite: "Test AutoscalingRunnerSet Labels" +templates: + - autoscalingrunnserset.yaml +tests: + - it: should render base labels + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "test-name" + namespace: "test-namespace" + chart: + appVersion: "0.14.0" + asserts: + - equal: + path: metadata.labels["helm.sh/chart"] + value: "gha-rs-0.14.0" + - equal: + path: metadata.labels["app.kubernetes.io/name"] + value: "test-name" + - equal: + path: metadata.labels["app.kubernetes.io/instance"] + value: "test-name" + - equal: + path: metadata.labels["app.kubernetes.io/component"] + value: "autoscaling-runner-set" + - equal: + path: metadata.labels["app.kubernetes.io/managed-by"] + value: "Helm" + - equal: + path: metadata.labels["app.kubernetes.io/part-of"] + value: "gha-rs" + - equal: + path: metadata.labels["app.kubernetes.io/version"] + value: "0.14.0" + - equal: + path: metadata.labels["actions.github.com/scale-set-name"] + value: "test-name" + - equal: + path: metadata.labels["actions.github.com/scale-set-namespace"] + value: "test-namespace" + + - it: should include user-defined labels + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + resource: + autoscalingRunnerSet: + metadata: + labels: + team: "backend" + environment: "production" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: metadata.labels["team"] + value: "backend" + - equal: + path: metadata.labels["environment"] + value: "production" + - equal: + path: metadata.labels["helm.sh/chart"] + value: "gha-rs-0.14.0" + - equal: + path: metadata.labels["app.kubernetes.io/name"] + value: "test-name" + - equal: + path: metadata.labels["app.kubernetes.io/instance"] + value: "test-name" + - equal: + path: metadata.labels["app.kubernetes.io/component"] + value: "autoscaling-runner-set" + - equal: + path: metadata.labels["app.kubernetes.io/managed-by"] + value: "Helm" + - equal: + path: metadata.labels["app.kubernetes.io/part-of"] + value: "gha-rs" + - equal: + path: metadata.labels["app.kubernetes.io/version"] + value: "0.14.0" + - equal: + path: metadata.labels["actions.github.com/scale-set-name"] + value: "test-name" + - equal: + path: metadata.labels["actions.github.com/scale-set-namespace"] + value: "test-namespace" + + - it: should include global labels + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + resource: + all: + metadata: + labels: + global-team: "platform" + owner: "devops" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: metadata.labels["global-team"] + value: "platform" + - equal: + path: metadata.labels["owner"] + value: "devops" + - equal: + path: metadata.labels["helm.sh/chart"] + value: "gha-rs-0.14.0" + - equal: + path: metadata.labels["app.kubernetes.io/name"] + value: "test-name" + - equal: + path: metadata.labels["app.kubernetes.io/instance"] + value: "test-name" + - equal: + path: metadata.labels["app.kubernetes.io/component"] + value: "autoscaling-runner-set" + - equal: + path: metadata.labels["app.kubernetes.io/managed-by"] + value: "Helm" + - equal: + path: metadata.labels["app.kubernetes.io/part-of"] + value: "gha-rs" + - equal: + path: metadata.labels["app.kubernetes.io/version"] + value: "0.14.0" + - equal: + path: metadata.labels["actions.github.com/scale-set-name"] + value: "test-name" + - equal: + path: metadata.labels["actions.github.com/scale-set-namespace"] + value: "test-namespace" + + - it: should merge both user and global labels + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + resource: + autoscalingRunnerSet: + metadata: + labels: + team: "backend" + environment: "staging" + all: + metadata: + labels: + global-team: "platform" + environment: "production" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: metadata.labels["team"] + value: "backend" + - equal: + path: metadata.labels["global-team"] + value: "platform" + - equal: + path: metadata.labels["environment"] + value: "staging" + - equal: + path: metadata.labels["helm.sh/chart"] + value: "gha-rs-0.14.0" + - equal: + path: metadata.labels["app.kubernetes.io/name"] + value: "test-name" + - equal: + path: metadata.labels["app.kubernetes.io/instance"] + value: "test-name" + - equal: + path: metadata.labels["app.kubernetes.io/component"] + value: "autoscaling-runner-set" + - equal: + path: metadata.labels["app.kubernetes.io/managed-by"] + value: "Helm" + - equal: + path: metadata.labels["app.kubernetes.io/part-of"] + value: "gha-rs" + - equal: + path: metadata.labels["app.kubernetes.io/version"] + value: "0.14.0" + - equal: + path: metadata.labels["actions.github.com/scale-set-name"] + value: "test-name" + - equal: + path: metadata.labels["actions.github.com/scale-set-namespace"] + value: "test-namespace" + + - it: should allow user labels to override global labels + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + resource: + autoscalingRunnerSet: + metadata: + labels: + tier: "frontend" + cost-center: "100" + all: + metadata: + labels: + tier: "backend" + environment: "staging" + cost-center: "200" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: metadata.labels["tier"] + value: "frontend" + - equal: + path: metadata.labels["cost-center"] + value: "100" + - equal: + path: metadata.labels["environment"] + value: "staging" + - equal: + path: metadata.labels["app.kubernetes.io/name"] + value: "test-name" + + - it: should preserve actions.github.com custom labels from user config + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + resource: + autoscalingRunnerSet: + metadata: + labels: + team: "backend" + actions.github.com/custom-label: "user-value" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: metadata.labels["team"] + value: "backend" + - notExists: + path: metadata.labels["actions.github.com/custom-label"] + - equal: + path: metadata.labels["actions.github.com/scale-set-name"] + value: "test-name" + + - it: should preserve actions.github.com custom labels from global config + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + resource: + all: + metadata: + labels: + owner: "devops" + actions.github.com/global-custom: "global-value" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: metadata.labels["owner"] + value: "devops" + - notExists: + path: metadata.labels["actions.github.com/global-custom"] + - equal: + path: metadata.labels["actions.github.com/scale-set-name"] + value: "test-name" \ No newline at end of file diff --git a/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_listener_metrics_test.yaml b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_listener_metrics_test.yaml new file mode 100644 index 00000000..3a2b4ff8 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_listener_metrics_test.yaml @@ -0,0 +1,55 @@ +suite: "Test AutoscalingRunnerSet Listener Metrics" +templates: + - autoscalingrunnserset.yaml +tests: + - it: should not render listenerMetrics when not configured + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - notExists: + path: spec.listenerMetrics + + - it: should render listenerMetrics when configured + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + listenerMetrics: + counters: + gha_started_jobs_total: + labels: + - repository + - organization + histograms: + gha_job_startup_duration_seconds: + buckets: + - 0.1 + - 1 + - 2.5 + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - exists: + path: spec.listenerMetrics + - equal: + path: spec.listenerMetrics.counters.gha_started_jobs_total.labels[0] + value: repository + - equal: + path: spec.listenerMetrics.counters.gha_started_jobs_total.labels[1] + value: organization + - contains: + path: spec.listenerMetrics.histograms.gha_job_startup_duration_seconds.buckets + content: 0.1 + - contains: + path: spec.listenerMetrics.histograms.gha_job_startup_duration_seconds.buckets + content: 2.5 diff --git a/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_listener_pod_template_test.yaml b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_listener_pod_template_test.yaml new file mode 100644 index 00000000..9a9373d5 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_listener_pod_template_test.yaml @@ -0,0 +1,28 @@ +suite: "AutoscalingRunnerSet listenerPodTemplate" +templates: + - autoscalingrunnserset.yaml +tests: + - it: should render listenerTemplate from listenerPodTemplate + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + listenerPodTemplate: + spec: + containers: + - name: listener + image: "ghcr.io/actions/actions-runner-controller/actionsmetricsserver:latest" + securityContext: + runAsUser: 1000 + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.listenerTemplate.spec.containers[0].name + value: listener + - equal: + path: spec.listenerTemplate.spec.containers[0].securityContext.runAsUser + value: 1000 diff --git a/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_min_max_runners_test.yaml b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_min_max_runners_test.yaml new file mode 100644 index 00000000..056da2ad --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_min_max_runners_test.yaml @@ -0,0 +1,71 @@ +suite: "Test AutoscalingRunnerSet MinMax Runners" +templates: + - autoscalingrunnserset.yaml +tests: + - it: should set minRunners and maxRunners correctly + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + scaleset.minRunners: 2 + scaleset.maxRunners: 5 + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.minRunners + value: 2 + - equal: + path: spec.maxRunners + value: 5 + - it: should fail when minRunners is greater than maxRunners + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + scaleset.minRunners: 6 + scaleset.maxRunners: 5 + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - failedTemplate: {} + - it: should work when minRunners equals maxRunners + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + scaleset.minRunners: 5 + scaleset.maxRunners: 5 + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.minRunners + value: 5 + - equal: + path: spec.maxRunners + value: 5 + - it: should not set minRunners and maxRunners when not provided + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - notExists: + path: spec.minRunners + - notExists: + path: spec.maxRunners \ No newline at end of file diff --git a/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_proxy_test.yaml b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_proxy_test.yaml new file mode 100644 index 00000000..af0df4bc --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_proxy_test.yaml @@ -0,0 +1,290 @@ +suite: "Test AutoscalingRunnerSet Proxy Configuration" +templates: + - autoscalingrunnserset.yaml +tests: + - it: should not render proxy section when not configured + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - notExists: + path: spec.proxy + + - it: should render http proxy configuration + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + proxy: + http: + url: "http://proxy.example.com:3128" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.proxy.http.url + value: "http://proxy.example.com:3128" + - notExists: + path: spec.proxy.https + + - it: should render https proxy configuration + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + proxy: + https: + url: "https://secure-proxy.example.com:3128" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.proxy.https.url + value: "https://secure-proxy.example.com:3128" + - notExists: + path: spec.proxy.http + + - it: should render both http and https proxy configuration + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + proxy: + http: + url: "http://proxy.example.com:3128" + https: + url: "https://secure-proxy.example.com:3128" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.proxy.http.url + value: "http://proxy.example.com:3128" + - equal: + path: spec.proxy.https.url + value: "https://secure-proxy.example.com:3128" + + - it: should render http proxy with credential secret reference + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + proxy: + http: + url: "http://proxy.example.com:3128" + credentialSecretRef: "proxy-credentials" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.proxy.http.url + value: "http://proxy.example.com:3128" + - equal: + path: spec.proxy.http.credentialSecretRef + value: "proxy-credentials" + + - it: should render https proxy with credential secret reference + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + proxy: + https: + url: "https://secure-proxy.example.com:3128" + credentialSecretRef: "secure-proxy-creds" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.proxy.https.url + value: "https://secure-proxy.example.com:3128" + - equal: + path: spec.proxy.https.credentialSecretRef + value: "secure-proxy-creds" + + - it: should render proxy with noProxy list + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + proxy: + http: + url: "http://proxy.example.com:3128" + noProxy: + - "localhost" + - "127.0.0.1" + - ".example.local" + - "10.0.0.0/8" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.proxy.http.url + value: "http://proxy.example.com:3128" + - contains: + path: spec.proxy.noProxy + content: "localhost" + - contains: + path: spec.proxy.noProxy + content: "127.0.0.1" + - contains: + path: spec.proxy.noProxy + content: ".example.local" + - contains: + path: spec.proxy.noProxy + content: "10.0.0.0/8" + + - it: should render complete proxy configuration with all options + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + proxy: + http: + url: "http://proxy.example.com:3128" + credentialSecretRef: "proxy-credentials" + https: + url: "https://secure-proxy.example.com:3128" + credentialSecretRef: "secure-proxy-creds" + noProxy: + - "localhost" + - "127.0.0.1" + - ".local" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.proxy.http.url + value: "http://proxy.example.com:3128" + - equal: + path: spec.proxy.http.credentialSecretRef + value: "proxy-credentials" + - equal: + path: spec.proxy.https.url + value: "https://secure-proxy.example.com:3128" + - equal: + path: spec.proxy.https.credentialSecretRef + value: "secure-proxy-creds" + - contains: + path: spec.proxy.noProxy + content: "localhost" + - contains: + path: spec.proxy.noProxy + content: "127.0.0.1" + - contains: + path: spec.proxy.noProxy + content: ".local" + + - it: should render proxy configuration with empty noProxy list + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + proxy: + http: + url: "http://proxy.example.com:3128" + noProxy: [] + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.proxy.http.url + value: "http://proxy.example.com:3128" + - notExists: + path: spec.proxy.noProxy + + - it: should not render proxy when configured as empty object + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + proxy: {} + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - notExists: + path: spec.proxy + + - it: should render proxy with only http without credentials + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + proxy: + http: + url: "http://unauthenticated-proxy.example.com:8080" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.proxy.http.url + value: "http://unauthenticated-proxy.example.com:8080" + - notExists: + path: spec.proxy.http.credentialSecretRef + - notExists: + path: spec.proxy.https + + - it: should render proxy with https and noProxy without http + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + proxy: + https: + url: "https://secure-proxy.example.com:3128" + noProxy: + - "internal.example.com" + - "*.local" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.proxy.https.url + value: "https://secure-proxy.example.com:3128" + - notExists: + path: spec.proxy.http + - contains: + path: spec.proxy.noProxy + content: "internal.example.com" + - contains: + path: spec.proxy.noProxy + content: "*.local" diff --git a/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_runner_pod_metadata_test.yaml b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_runner_pod_metadata_test.yaml new file mode 100644 index 00000000..2c5c9ddb --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_runner_pod_metadata_test.yaml @@ -0,0 +1,87 @@ +suite: "AutoscalingRunnerSet runner pod metadata" +templates: + - autoscalingrunnserset.yaml +tests: + - it: should render runner.pod.metadata labels and annotations merged with common/global metadata + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + resource: + all: + metadata: + labels: + team: "platform" + annotations: + global-annotation: "1" + runner: + pod: + metadata: + labels: + purpose: "ci" + annotations: + pod-annotation: "2" + release: + name: "test-name" + namespace: "test-namespace" + chart: + appVersion: "0.14.0" + asserts: + - equal: + path: spec.template.metadata.labels["purpose"] + value: "ci" + - equal: + path: spec.template.metadata.labels["team"] + value: "platform" + - equal: + path: spec.template.metadata.labels["helm.sh/chart"] + value: "gha-rs-0.14.0" + - equal: + path: spec.template.metadata.labels["app.kubernetes.io/name"] + value: "test-name" + - equal: + path: spec.template.metadata.labels["app.kubernetes.io/managed-by"] + value: "Helm" + - equal: + path: spec.template.metadata.labels["actions.github.com/scale-set-name"] + value: "test-name" + - equal: + path: spec.template.metadata.annotations["global-annotation"] + value: "1" + - equal: + path: spec.template.metadata.annotations["pod-annotation"] + value: "2" + + - it: should drop reserved actions.github.com/* keys from runner.pod.metadata + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + pod: + metadata: + labels: + actions.github.com/scale-set-name: "should-not-override" + ok: "yes" + annotations: + actions.github.com/some-annotation: "should-not-appear" + ok-annotation: "yes" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.template.metadata.labels["actions.github.com/scale-set-name"] + value: "test-name" + - equal: + path: spec.template.metadata.labels["ok"] + value: "yes" + - notExists: + path: spec.template.metadata.annotations["actions.github.com/some-annotation"] + - equal: + path: spec.template.metadata.annotations["ok-annotation"] + value: "yes" diff --git a/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_runner_pod_spec_init_containers_validation_test.yaml b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_runner_pod_spec_init_containers_validation_test.yaml new file mode 100644 index 00000000..d666694d --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_runner_pod_spec_init_containers_validation_test.yaml @@ -0,0 +1,45 @@ +suite: autoscaling runner set runner pod spec initContainers validation + +templates: + - templates/autoscalingrunnserset.yaml + +tests: + - it: should fail when runner.pod.spec.initContainers is not a list + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + mode: "kubernetes" + pod: + spec: + initContainers: + name: "not-a-list" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - failedTemplate: + errorMessage: ".Values.runner.pod.spec.initContainers must be a list of container specifications" + + - it: should fail when initContainers entry has no name + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + mode: "kubernetes" + pod: + spec: + initContainers: + - image: "busybox:1.36" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - failedTemplate: + errorMessage: ".Values.runner.pod.spec.initContainers[].name is required" diff --git a/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_runner_pod_spec_passthrough_fields_test.yaml b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_runner_pod_spec_passthrough_fields_test.yaml new file mode 100644 index 00000000..ed679bca --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_runner_pod_spec_passthrough_fields_test.yaml @@ -0,0 +1,45 @@ +suite: AutoscalingRunnerSet runner pod spec passthrough fields + +templates: + - templates/autoscalingrunnserset.yaml + +tests: + - it: should apply additional runner.pod.spec fields to the runner pod spec + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + mode: "kubernetes" + pod: + spec: + nodeSelector: + disktype: ssd + tolerations: + - key: "dedicated" + operator: "Equal" + value: "ci" + effect: "NoSchedule" + priorityClassName: "runner-high" + terminationGracePeriodSeconds: 42 + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.template.spec.nodeSelector.disktype + value: ssd + - equal: + path: spec.template.spec.tolerations[0].key + value: dedicated + - equal: + path: spec.template.spec.tolerations[0].value + value: ci + - equal: + path: spec.template.spec.priorityClassName + value: runner-high + - equal: + path: spec.template.spec.terminationGracePeriodSeconds + value: 42 diff --git a/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_runner_pod_spec_volumes_validation_test.yaml b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_runner_pod_spec_volumes_validation_test.yaml new file mode 100644 index 00000000..10530603 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_runner_pod_spec_volumes_validation_test.yaml @@ -0,0 +1,25 @@ +suite: autoscaling runner set runner pod spec volumes validation + +templates: + - templates/autoscalingrunnserset.yaml + +tests: + - it: should fail when runner.pod.spec.volumes is not a list + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + runner: + mode: "kubernetes" + pod: + spec: + volumes: + name: "not-a-list" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - failedTemplate: + errorMessage: ".Values.runner.pod.spec.volumes must be a list of volume specifications" diff --git a/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_vault_config_test.yaml b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_vault_config_test.yaml new file mode 100644 index 00000000..a5f93c40 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/autoscaling_runner_set_vault_config_test.yaml @@ -0,0 +1,110 @@ +suite: "Test AutoscalingRunnerSet Vault Config" +templates: + - autoscalingrunnserset.yaml +tests: + - it: should not render vaultConfig when secretResolution.type is kubernetes + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + secretResolution: + type: kubernetes + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - notExists: + path: spec.vaultConfig + + - it: should render azureKeyVault vaultConfig when configured + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + secretResolution: + type: azureKeyVault + azureKeyVault: + url: "https://myvault.vault.azure.net" + tenantId: "tenant-123" + clientId: "client-456" + certificatePath: "/etc/certs/akv.pem" + secretKey: "secret-key-name" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.vaultConfig.type + value: azureKeyVault + - equal: + path: spec.vaultConfig.azureKeyVault.url + value: "https://myvault.vault.azure.net" + - equal: + path: spec.vaultConfig.azureKeyVault.tenantId + value: "tenant-123" + - equal: + path: spec.vaultConfig.azureKeyVault.clientId + value: "client-456" + - equal: + path: spec.vaultConfig.azureKeyVault.certificatePath + value: "/etc/certs/akv.pem" + - equal: + path: spec.vaultConfig.azureKeyVault.secretKey + value: "secret-key-name" + + - it: should render vaultConfig proxy when configured + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + secretResolution: + type: azureKeyVault + proxy: + http: + url: "http://proxy.example.com:3128" + credentialSecretRef: "proxy-credentials" + noProxy: + - "localhost" + azureKeyVault: + url: "https://myvault.vault.azure.net" + tenantId: "tenant-123" + clientId: "client-456" + certificatePath: "/etc/certs/akv.pem" + secretKey: "secret-key-name" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: spec.vaultConfig.proxy.http.url + value: "http://proxy.example.com:3128" + - equal: + path: spec.vaultConfig.proxy.http.credentialSecretRef + value: "proxy-credentials" + - contains: + path: spec.vaultConfig.proxy.noProxy + content: "localhost" + - notExists: + path: spec.proxy + + - it: should fail for unsupported secretResolution.type + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + secretResolution: + type: "hashicorpVault" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - failedTemplate: + errorMessage: "Unsupported keyVault type: hashicorpVault" diff --git a/charts/gha-runner-scale-set-dev/tests/github_secret_annotations_test.yaml b/charts/gha-runner-scale-set-dev/tests/github_secret_annotations_test.yaml new file mode 100644 index 00000000..8b195ca8 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/github_secret_annotations_test.yaml @@ -0,0 +1,49 @@ +suite: "Test GitHub Secret Annotations" +templates: + - githubsecret.yaml +tests: + - it: should include global annotations + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + resource: + all: + metadata: + annotations: + a: "global" + shared: "global" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: metadata.annotations.a + value: "global" + - equal: + path: metadata.annotations.shared + value: "global" + + - it: should drop actions.github.com annotations from global config + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + resource: + all: + metadata: + annotations: + ok: "ok" + actions.github.com/values-hash: "user-value" + actions.github.com/cleanup-something: "should-not-render" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: metadata.annotations.ok + value: "ok" + - notExists: + path: metadata.annotations["actions.github.com/values-hash"] + - notExists: + path: metadata.annotations["actions.github.com/cleanup-something"] diff --git a/charts/gha-runner-scale-set-dev/tests/github_secret_app_test.yaml b/charts/gha-runner-scale-set-dev/tests/github_secret_app_test.yaml new file mode 100644 index 00000000..b1dece00 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/github_secret_app_test.yaml @@ -0,0 +1,70 @@ +suite: "Test GitHub Secret Data" +templates: + - githubsecret.yaml +tests: + - it: should render PAT token when configured + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - exists: + path: data.github_token + - equal: + path: data.github_token + value: "Z2hfdG9rZW4xMjM0NQ==" + - notExists: + path: data.github_app_id + - notExists: + path: data.github_app_installation_id + - notExists: + path: data.github_app_private_key + + - it: should render GitHub App keys when app is configured + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.app: + clientId: "123" + installationId: "456" + privateKey: "mykey" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - notExists: + path: data.github_token + - equal: + path: data.github_app_id + value: "MTIz" + - equal: + path: data.github_app_installation_id + value: "NDU2" + - equal: + path: data.github_app_private_key + value: "bXlrZXk=" + + - it: should fail if app is configured without installationId + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.app: + clientId: "123" + privateKey: "mykey" + asserts: + - failedTemplate: + errorMessage: ".Values.auth.app.installationId is required when using GitHub App auth" + + - it: should fail if app is configured without privateKey + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.app: + clientId: "123" + installationId: "456" + asserts: + - failedTemplate: + errorMessage: ".Values.auth.app.privateKey is required when using GitHub App auth" diff --git a/charts/gha-runner-scale-set-dev/tests/github_secret_labels_test.yaml b/charts/gha-runner-scale-set-dev/tests/github_secret_labels_test.yaml new file mode 100644 index 00000000..9e60a70c --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/github_secret_labels_test.yaml @@ -0,0 +1,129 @@ +suite: "Test GitHub Secret Labels" +templates: + - githubsecret.yaml +tests: + - it: should render base labels + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + release: + name: "test-name" + namespace: "test-namespace" + chart: + appVersion: "0.14.0" + asserts: + - equal: + path: metadata.labels["helm.sh/chart"] + value: "gha-rs-0.14.0" + - equal: + path: metadata.labels["app.kubernetes.io/name"] + value: "test-name" + - equal: + path: metadata.labels["app.kubernetes.io/instance"] + value: "test-name" + - equal: + path: metadata.labels["app.kubernetes.io/component"] + value: "github-secret" + - equal: + path: metadata.labels["app.kubernetes.io/managed-by"] + value: "Helm" + - equal: + path: metadata.labels["app.kubernetes.io/part-of"] + value: "gha-rs" + - equal: + path: metadata.labels["app.kubernetes.io/version"] + value: "0.14.0" + - equal: + path: metadata.labels["actions.github.com/scale-set-name"] + value: "test-name" + - equal: + path: metadata.labels["actions.github.com/scale-set-namespace"] + value: "test-namespace" + + - it: should include global labels + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + resource: + all: + metadata: + labels: + global-team: "platform" + owner: "devops" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: metadata.labels["global-team"] + value: "platform" + - equal: + path: metadata.labels["owner"] + value: "devops" + - equal: + path: metadata.labels["app.kubernetes.io/component"] + value: "github-secret" + + - it: should drop actions.github.com custom labels from global config + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + resource: + all: + metadata: + labels: + owner: "devops" + actions.github.com/global-custom: "global-value" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: metadata.labels["owner"] + value: "devops" + - notExists: + path: metadata.labels["actions.github.com/global-custom"] + - equal: + path: metadata.labels["actions.github.com/scale-set-name"] + value: "test-name" + + - it: should not allow global labels to override reserved labels + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_token12345" + resource: + all: + metadata: + labels: + helm.sh/chart: "bad" + app.kubernetes.io/name: "bad" + app.kubernetes.io/instance: "bad" + app.kubernetes.io/component: "bad" + actions.github.com/scale-set-name: "bad" + actions.github.com/scale-set-namespace: "bad" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: metadata.labels["helm.sh/chart"] + value: "gha-rs-0.14.0" + - equal: + path: metadata.labels["app.kubernetes.io/name"] + value: "test-name" + - equal: + path: metadata.labels["app.kubernetes.io/instance"] + value: "test-name" + - equal: + path: metadata.labels["app.kubernetes.io/component"] + value: "github-secret" + - equal: + path: metadata.labels["actions.github.com/scale-set-name"] + value: "test-name" + - equal: + path: metadata.labels["actions.github.com/scale-set-namespace"] + value: "test-namespace" diff --git a/charts/gha-runner-scale-set-dev/tests/kube_mode_role_binding_test.yaml b/charts/gha-runner-scale-set-dev/tests/kube_mode_role_binding_test.yaml new file mode 100644 index 00000000..1d915603 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/kube_mode_role_binding_test.yaml @@ -0,0 +1,182 @@ +suite: "Test Kubernetes Mode RoleBinding" +templates: + - kube_mode_role_binding.yaml +tests: + - it: should render base rolebinding metadata in kubernetes mode + set: + runner: + mode: "kubernetes" + kubernetesMode: + default: true + serviceAccountName: "" + release: + name: "test-name" + namespace: "test-namespace" + chart: + appVersion: "0.14.0" + asserts: + - equal: + path: apiVersion + value: "rbac.authorization.k8s.io/v1" + - equal: + path: kind + value: "RoleBinding" + - equal: + path: metadata.name + value: "test-name-kube-mode" + - equal: + path: metadata.namespace + value: "test-namespace" + - equal: + path: metadata.labels["app.kubernetes.io/component"] + value: "kube-mode-role-binding" + - equal: + path: metadata.labels["actions.github.com/scale-set-name"] + value: "test-name" + - equal: + path: metadata.labels["actions.github.com/scale-set-namespace"] + value: "test-namespace" + - equal: + path: metadata.finalizers[0] + value: "actions.github.com/cleanup-protection" + - equal: + path: roleRef.kind + value: "Role" + - equal: + path: roleRef.name + value: "test-name-kube-mode" + - equal: + path: subjects[0].kind + value: "ServiceAccount" + - equal: + path: subjects[0].name + value: "test-name-kube-mode" + - equal: + path: subjects[0].namespace + value: "test-namespace" + + - it: should not render when runner mode is not kubernetes + set: + runner: + mode: "dind" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - hasDocuments: + count: 0 + + - it: should not render when serviceAccountName is provided + set: + runner: + mode: "kubernetes" + kubernetesMode: + default: true + serviceAccountName: "custom-sa" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - hasDocuments: + count: 0 + + - it: should include global and resource labels + set: + runner: + mode: "kubernetes" + kubernetesMode: + default: true + serviceAccountName: "" + resource: + all: + metadata: + labels: + global-team: "platform" + kubernetesModeRoleBinding: + metadata: + labels: + rb-team: "arc" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: metadata.labels["global-team"] + value: "platform" + - equal: + path: metadata.labels["rb-team"] + value: "arc" + - equal: + path: metadata.labels["app.kubernetes.io/component"] + value: "kube-mode-role-binding" + + - it: should drop actions.github.com custom labels from config + set: + runner: + mode: "kubernetes" + kubernetesMode: + default: true + serviceAccountName: "" + resource: + all: + metadata: + labels: + owner: "devops" + actions.github.com/global-custom: "global-value" + kubernetesModeRoleBinding: + metadata: + labels: + actions.github.com/rb-custom: "rb-value" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: metadata.labels["owner"] + value: "devops" + - notExists: + path: metadata.labels["actions.github.com/global-custom"] + - notExists: + path: metadata.labels["actions.github.com/rb-custom"] + + - it: should not allow overriding reserved labels + set: + runner: + mode: "kubernetes" + kubernetesMode: + default: true + serviceAccountName: "" + resource: + all: + metadata: + labels: + helm.sh/chart: "bad" + app.kubernetes.io/name: "bad" + app.kubernetes.io/instance: "bad" + app.kubernetes.io/component: "bad" + actions.github.com/scale-set-name: "bad" + actions.github.com/scale-set-namespace: "bad" + release: + name: "test-name" + namespace: "test-namespace" + chart: + appVersion: "0.14.0" + asserts: + - equal: + path: metadata.labels["helm.sh/chart"] + value: "gha-rs-0.14.0" + - equal: + path: metadata.labels["app.kubernetes.io/name"] + value: "test-name" + - equal: + path: metadata.labels["app.kubernetes.io/instance"] + value: "test-name" + - equal: + path: metadata.labels["app.kubernetes.io/component"] + value: "kube-mode-role-binding" + - equal: + path: metadata.labels["actions.github.com/scale-set-name"] + value: "test-name" + - equal: + path: metadata.labels["actions.github.com/scale-set-namespace"] + value: "test-namespace" diff --git a/charts/gha-runner-scale-set-dev/tests/kube_mode_role_test.yaml b/charts/gha-runner-scale-set-dev/tests/kube_mode_role_test.yaml new file mode 100644 index 00000000..8657d5a5 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/kube_mode_role_test.yaml @@ -0,0 +1,117 @@ +suite: "Test Kubernetes Mode Role" +templates: + - kube_mode_role.yaml +tests: + - it: should render base role metadata in kubernetes mode + set: + runner: + mode: "kubernetes" + kubernetesMode: + default: true + serviceAccountName: "" + release: + name: "test-name" + namespace: "test-namespace" + chart: + appVersion: "0.14.0" + asserts: + - equal: + path: apiVersion + value: "rbac.authorization.k8s.io/v1" + - equal: + path: kind + value: "Role" + - equal: + path: metadata.name + value: "test-name-kube-mode" + - equal: + path: metadata.namespace + value: "test-namespace" + - equal: + path: metadata.labels["app.kubernetes.io/component"] + value: "kube-mode-role" + - equal: + path: metadata.labels["actions.github.com/scale-set-name"] + value: "test-name" + - equal: + path: metadata.labels["actions.github.com/scale-set-namespace"] + value: "test-namespace" + - equal: + path: metadata.finalizers[0] + value: "actions.github.com/cleanup-protection" + + - it: should append extra RBAC policy rules + set: + runner: + mode: "kubernetes" + kubernetesMode: + default: true + serviceAccountName: "" + resource: + kubernetesModeRole: + extraRules: + - apiGroups: + - "" + resources: + - "events" + verbs: + - "create" + - "patch" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: rules[5].apiGroups[0] + value: "" + - equal: + path: rules[5].resources[0] + value: "events" + - equal: + path: rules[5].verbs[0] + value: "create" + - equal: + path: rules[5].verbs[1] + value: "patch" + + - it: should fail when extraRules is not a list + set: + runner: + mode: "kubernetes" + kubernetesMode: + default: true + serviceAccountName: "" + resource: + kubernetesModeRole: + extraRules: "not-a-list" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - failedTemplate: + errorMessage: ".Values.resource.kubernetesModeRole.extraRules must be a list of RBAC policy rules" + + - it: should not render when runner mode is not kubernetes + set: + runner: + mode: "dind" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - hasDocuments: + count: 0 + + - it: should not render when serviceAccountName is provided + set: + runner: + mode: "kubernetes" + kubernetesMode: + default: true + serviceAccountName: "custom-sa" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - hasDocuments: + count: 0 diff --git a/charts/gha-runner-scale-set-dev/tests/kube_mode_serviceaccount_test.yaml b/charts/gha-runner-scale-set-dev/tests/kube_mode_serviceaccount_test.yaml new file mode 100644 index 00000000..8cf23d59 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/kube_mode_serviceaccount_test.yaml @@ -0,0 +1,167 @@ +suite: "Test Kubernetes Mode ServiceAccount" +templates: + - kube_mode_serviceaccount.yaml +tests: + - it: should render base serviceaccount metadata in kubernetes mode + set: + runner: + mode: "kubernetes" + kubernetesMode: + default: true + serviceAccountName: "" + release: + name: "test-name" + namespace: "test-namespace" + chart: + appVersion: "0.14.0" + asserts: + - equal: + path: apiVersion + value: "v1" + - equal: + path: kind + value: "ServiceAccount" + - equal: + path: metadata.name + value: "test-name-kube-mode" + - equal: + path: metadata.namespace + value: "test-namespace" + - equal: + path: metadata.labels["app.kubernetes.io/component"] + value: "kube-mode-serviceaccount" + - equal: + path: metadata.labels["actions.github.com/scale-set-name"] + value: "test-name" + - equal: + path: metadata.labels["actions.github.com/scale-set-namespace"] + value: "test-namespace" + - equal: + path: metadata.finalizers[0] + value: "actions.github.com/cleanup-protection" + + - it: should not render when runner mode is not kubernetes + set: + runner: + mode: "dind" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - hasDocuments: + count: 0 + + - it: should not render when serviceAccountName is provided + set: + runner: + mode: "kubernetes" + kubernetesMode: + default: true + serviceAccountName: "custom-sa" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - hasDocuments: + count: 0 + + - it: should include global and resource labels + set: + runner: + mode: "kubernetes" + kubernetesMode: + default: true + serviceAccountName: "" + resource: + all: + metadata: + labels: + global-team: "platform" + kubernetesModeServiceAccount: + metadata: + labels: + sa-team: "arc" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: metadata.labels["global-team"] + value: "platform" + - equal: + path: metadata.labels["sa-team"] + value: "arc" + - equal: + path: metadata.labels["app.kubernetes.io/component"] + value: "kube-mode-serviceaccount" + + - it: should drop actions.github.com custom labels from config + set: + runner: + mode: "kubernetes" + kubernetesMode: + default: true + serviceAccountName: "" + resource: + all: + metadata: + labels: + owner: "devops" + actions.github.com/global-custom: "global-value" + kubernetesModeServiceAccount: + metadata: + labels: + actions.github.com/sa-custom: "sa-value" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: metadata.labels["owner"] + value: "devops" + - notExists: + path: metadata.labels["actions.github.com/global-custom"] + - notExists: + path: metadata.labels["actions.github.com/sa-custom"] + + - it: should not allow overriding reserved labels + set: + runner: + mode: "kubernetes" + kubernetesMode: + default: true + serviceAccountName: "" + resource: + all: + metadata: + labels: + helm.sh/chart: "bad" + app.kubernetes.io/name: "bad" + app.kubernetes.io/instance: "bad" + app.kubernetes.io/component: "bad" + actions.github.com/scale-set-name: "bad" + actions.github.com/scale-set-namespace: "bad" + release: + name: "test-name" + namespace: "test-namespace" + chart: + appVersion: "0.14.0" + asserts: + - equal: + path: metadata.labels["helm.sh/chart"] + value: "gha-rs-0.14.0" + - equal: + path: metadata.labels["app.kubernetes.io/name"] + value: "test-name" + - equal: + path: metadata.labels["app.kubernetes.io/instance"] + value: "test-name" + - equal: + path: metadata.labels["app.kubernetes.io/component"] + value: "kube-mode-serviceaccount" + - equal: + path: metadata.labels["actions.github.com/scale-set-name"] + value: "test-name" + - equal: + path: metadata.labels["actions.github.com/scale-set-namespace"] + value: "test-namespace" diff --git a/charts/gha-runner-scale-set-dev/tests/manager_role_binding_annotations_test.yaml b/charts/gha-runner-scale-set-dev/tests/manager_role_binding_annotations_test.yaml new file mode 100644 index 00000000..8a5af545 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/manager_role_binding_annotations_test.yaml @@ -0,0 +1,48 @@ +suite: "Test Manager RoleBinding Annotations" +templates: + - manager_role_binding.yaml +tests: + - it: should merge global and resource annotations (resource wins) + set: + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + resource: + all: + metadata: + annotations: + owner: "platform" + environment: "production" + managerRoleBinding: + metadata: + annotations: + environment: "staging" + team: "backend" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: metadata.annotations["owner"] + value: "platform" + - equal: + path: metadata.annotations["team"] + value: "backend" + - equal: + path: metadata.annotations["environment"] + value: "staging" + + - it: should not allow actions.github.com/* annotations from user input + set: + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + resource: + managerRoleBinding: + metadata: + annotations: + actions.github.com/something: "overridden" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - notExists: + path: metadata.annotations["actions.github.com/something"] diff --git a/charts/gha-runner-scale-set-dev/tests/manager_role_binding_labels_test.yaml b/charts/gha-runner-scale-set-dev/tests/manager_role_binding_labels_test.yaml new file mode 100644 index 00000000..9734387f --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/manager_role_binding_labels_test.yaml @@ -0,0 +1,91 @@ +suite: "Test Manager RoleBinding Labels" +templates: + - manager_role_binding.yaml +tests: + - it: should render base labels + set: + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + release: + name: "test-name" + namespace: "test-namespace" + chart: + appVersion: "0.14.0" + asserts: + - equal: + path: metadata.labels["helm.sh/chart"] + value: "gha-rs-0.14.0" + - equal: + path: metadata.labels["app.kubernetes.io/name"] + value: "test-name" + - equal: + path: metadata.labels["app.kubernetes.io/instance"] + value: "test-name" + - equal: + path: metadata.labels["app.kubernetes.io/component"] + value: "manager-role-binding" + - equal: + path: metadata.labels["app.kubernetes.io/managed-by"] + value: "Helm" + - equal: + path: metadata.labels["app.kubernetes.io/part-of"] + value: "gha-rs" + - equal: + path: metadata.labels["app.kubernetes.io/version"] + value: "0.14.0" + - equal: + path: metadata.labels["actions.github.com/scale-set-name"] + value: "test-name" + - equal: + path: metadata.labels["actions.github.com/scale-set-namespace"] + value: "test-namespace" + + - it: should merge global and resource labels (resource wins) + set: + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + resource: + all: + metadata: + labels: + owner: "platform" + environment: "production" + managerRoleBinding: + metadata: + labels: + environment: "staging" + team: "backend" + release: + name: "test-name" + namespace: "test-namespace" + chart: + appVersion: "0.14.0" + asserts: + - equal: + path: metadata.labels["owner"] + value: "platform" + - equal: + path: metadata.labels["team"] + value: "backend" + - equal: + path: metadata.labels["environment"] + value: "staging" + + - it: should not allow actions.github.com/* labels from user input + set: + controllerServiceAccount.name: "arc" + controllerServiceAccount.namespace: "arc-system" + resource: + managerRoleBinding: + metadata: + labels: + actions.github.com/scale-set-name: "overridden" + release: + name: "test-name" + namespace: "test-namespace" + chart: + appVersion: "0.14.0" + asserts: + - equal: + path: metadata.labels["actions.github.com/scale-set-name"] + value: "test-name" diff --git a/charts/gha-runner-scale-set-dev/tests/manager_role_extra_rules_test.yaml b/charts/gha-runner-scale-set-dev/tests/manager_role_extra_rules_test.yaml new file mode 100644 index 00000000..259563ae --- /dev/null +++ b/charts/gha-runner-scale-set-dev/tests/manager_role_extra_rules_test.yaml @@ -0,0 +1,76 @@ +suite: "Test Manager Role Extra Rules" +templates: + - manager_role.yaml +tests: + - it: should render base role metadata + release: + name: "test-name" + namespace: "test-namespace" + chart: + appVersion: "0.14.0" + asserts: + - equal: + path: apiVersion + value: "rbac.authorization.k8s.io/v1" + - equal: + path: kind + value: "Role" + - equal: + path: metadata.name + value: "test-name-manager-role" + - equal: + path: metadata.namespace + value: "test-namespace" + - equal: + path: metadata.labels["app.kubernetes.io/component"] + value: "manager-role" + - equal: + path: metadata.labels["actions.github.com/scale-set-name"] + value: "test-name" + - equal: + path: metadata.labels["actions.github.com/scale-set-namespace"] + value: "test-namespace" + - equal: + path: metadata.finalizers[0] + value: "actions.github.com/cleanup-protection" + + - it: should append extra RBAC policy rules + set: + resource: + managerRole: + extraRules: + - apiGroups: + - "" + resources: + - "events" + verbs: + - "create" + - "patch" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - equal: + path: rules[6].apiGroups[0] + value: "" + - equal: + path: rules[6].resources[0] + value: "events" + - equal: + path: rules[6].verbs[0] + value: "create" + - equal: + path: rules[6].verbs[1] + value: "patch" + + - it: should fail when extraRules is not a list + set: + resource: + managerRole: + extraRules: "not-a-list" + release: + name: "test-name" + namespace: "test-namespace" + asserts: + - failedTemplate: + errorMessage: ".Values.resource.managerRole.extraRules must be a list of RBAC policy rules" diff --git a/charts/gha-runner-scale-set-dev/values.yaml b/charts/gha-runner-scale-set-dev/values.yaml new file mode 100644 index 00000000..096b2c33 --- /dev/null +++ b/charts/gha-runner-scale-set-dev/values.yaml @@ -0,0 +1,383 @@ +## By default .Release.namespace is used +namespaceOverride: "" + +scaleset: + # Name of the scaleset + name: "" + runnerGroup: "default" + ## minRunners is the min number of idle runners. The target number of runners created will be + ## calculated as a sum of minRunners and the number of jobs assigned to the scale set. + # minRunners: 0 + ## maxRunners is the max number of runners the autoscaling runner set will scale up to. + # maxRunners: 5 + +# Auth object provides authorization parameters. +# You should apply either: +# 1) secretName referencing the secret containing authorization parameters in the same namespace where the scale set is being installed in +# 2) app object parameters +# 3) github_tokne +# +# If multiple of them are set, only single one will be applied based on the above mentioned order. +auth: + url: "" # Required + githubToken: "" + secretName: "" + app: + clientId: "" + installationId: "" + privateKey: "" + +# secretResolution configures how secrets are resolved for this scale set. +# By default, secrets are resolved using Kubernetes secrets. When Kubernetes +# secrets are used, no proxy config will be applied. +# +# If you decide to use secret integrations with vaults, you can configure +# proxy settings for the vault communication here. +secretResolution: + # Name of the secret resolver to use. + # Available values: + # - "kubernetes" - use Kubernetes secrets + # - "azureKeyVault" - use Azure Key Vault + type: "kubernetes" + + ## Proxy settings when type is NOT "kubernetes" + # proxy: + # http: + # url: http://proxy.com:1234 + # credentialSecretRef: proxy-auth # a secret with `username` and `password` keys + # https: + # url: http://proxy.com:1234 + # credentialSecretRef: proxy-auth # a secret with `username` and `password` keys + # noProxy: + # - example.com + # - example.org + + ## Configuration for Azure Key Vault integration + # azureKeyVault: + # url: "" + # client_id: "" + # tenant_id: "" + # certificate_path: "" + +## Proxy can be used to define proxy settings that will be used by the +## controller, the listener and the runner of this scale set. +# proxy: +# http: +# url: http://proxy.com:1234 +# credentialSecretRef: proxy-auth # a secret with `username` and `password` keys +# https: +# url: http://proxy.com:1234 +# credentialSecretRef: proxy-auth # a secret with `username` and `password` keys +# noProxy: +# - example.com +# - example.org + +## listenerTemplate is the PodSpec for each listener Pod +## For reference: https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#PodSpec +# listenerPodTemplate: +# spec: +# containers: +# # Use this section to append additional configuration to the listener container. +# # If you change the name of the container, the configuration will not be applied to the listener, +# # and it will be treated as a side-car container. +# - name: listener +# securityContext: +# runAsUser: 1000 +# # Use this section to add the configuration of a side-car container. +# # Comment it out or remove it if you don't need it. +# # Spec for this container will be applied as is without any modifications. +# - name: side-car +# image: example-sidecar + +## Resource object allows modifying resources created by the chart itself +resource: + # Specifies metadata that will be applied to all resources managed by ARC + all: + metadata: + labels: {} + annotations: {} + + # Specifies metadata that will be applied to the AutoscalingRunnerSet resource + autoscalingRunnerSet: + metadata: + labels: {} + annotations: {} + + # Specifies metadata that will be applied to the manager Role resource + managerRole: + metadata: + labels: {} + annotations: {} + extraRules: [] + + # Specifies metadata that will be applied to the manager RoleBinding resource + managerRoleBinding: + metadata: + labels: {} + annotations: {} + + # Specifies metadata that will be applied to the no-permission ServiceAccount + # (created for non-kubernetes runner modes). + noPermissionServiceAccount: + metadata: + labels: {} + annotations: {} + + # Specifies metadata that will be applied to the kubernetes-mode RoleBinding + # (created when runner.mode is "kubernetes" and a ServiceAccountName is not provided). + kubernetesModeRoleBinding: + metadata: + labels: {} + annotations: {} + + # Specifies metadata that will be applied to the kubernetes-mode Role. + kubernetesModeRole: + metadata: + labels: {} + annotations: {} + extraRules: [] + + # Specifies metadata that will be applied to the kubernetes-mode ServiceAccount. + kubernetesModeServiceAccount: + metadata: + labels: {} + annotations: {} + + # TODO: Add more resource customizations when needed + +# Template applied for the runner container +runner: + # Mode can be used to automatically add configuration for the selected mode + # The available modes are: + # - "" (default) - no additional configuration is applied + # - "kubernetes" - configuration for running jobs in Kubernetes mode is applied + # - "dind" - configuration for running jobs in Docker-in-Docker mode is + # + # For each mode, we provide configuration out of the box that works for most use + # cases. You can customize our configuration by modifying the fields below, + # or you can leave mode empty and provide your own complete configuration. + mode: "" + + pod: + metadata: + labels: {} + annotations: {} + # Runner pod template customization. + # + # All fields under runner.pod.spec (except those listed below) are applied directly to + # spec.template.spec of the generated runner pod. + # + # Special handling: + # - spec.containers: appended after the generated "runner" container (name "runner" is reserved) + # - spec.initContainers: appended after any generated initContainers (e.g. dind mode) + # - spec.volumes: appended after generated volumes + # + # Note: serviceAccountName is managed by the chart and cannot be overridden via runner.pod.spec. + spec: + # The name "runner" is reserved and must not be used here. + containers: [] + initContainers: [] + volumes: [] + + # container field is applied to the container named "runner". You cannot override the name of the runner container + container: + image: "ghcr.io/actions/actions-runner:latest" + command: ["/home/runner/run.sh"] + + dind: + copyExternals: true + dockerGroupId: "123" + dockerSock: "unix:///var/run/docker.sock" + waitForDockerInSeconds: 120 + container: + image: "docker:dind" + + kubernetesMode: + serviceAccountName: "" + hookPath: "/home/runner/k8s/index.js" + requireJobContainer: true + extension: {} + + + +## A self-signed CA certificate for communication with the GitHub server can be +## provided using a config map key selector. If `runnerMountPath` is set, for +## each runner pod ARC will: +## - create a `github-server-tls-cert` volume containing the certificate +## specified in `certificateFrom` +## - mount that volume on path `runnerMountPath`/{certificate name} +## - set NODE_EXTRA_CA_CERTS environment variable to that same path +## - set RUNNER_UPDATE_CA_CERTS environment variable to "1" (as of version +## 2.303.0 this will instruct the runner to reload certificates on the host) +## +## If any of the above had already been set by the user in the runner pod +## template, ARC will observe those and not overwrite them. +## Example configuration: +# +# githubServerTLS: +# certificateFrom: +# configMapKeyRef: +# name: config-map-name +# key: ca.crt +# runnerMountPath: /usr/local/share/ca-certificates/ + +## controllerServiceAccount is the service account of the controller +controllerServiceAccount: + namespace: "" + name: "" + +## listenerMetrics are configurable metrics applied to the listener. +## In order to avoid helm merging these fields, we left the metrics commented out. +## When configuring metrics, please uncomment the listenerMetrics object below. +## You can modify the configuration to remove the label or specify custom buckets for histogram. +## +## If the buckets field is not specified, the default buckets will be applied. Default buckets are +## provided here for documentation purposes +# listenerMetrics: +# counters: +# gha_started_jobs_total: +# labels: +# ["repository", "organization", "enterprise", "job_name", "event_name", "job_workflow_ref", "job_workflow_name", "job_workflow_target"] +# gha_completed_jobs_total: +# labels: +# [ +# "repository", +# "organization", +# "enterprise", +# "job_name", +# "event_name", +# "job_result", +# "job_workflow_ref", +# "job_workflow_name", +# "job_workflow_target", +# ] +# gauges: +# gha_assigned_jobs: +# labels: ["name", "namespace", "repository", "organization", "enterprise"] +# gha_running_jobs: +# labels: ["name", "namespace", "repository", "organization", "enterprise"] +# gha_registered_runners: +# labels: ["name", "namespace", "repository", "organization", "enterprise"] +# gha_busy_runners: +# labels: ["name", "namespace", "repository", "organization", "enterprise"] +# gha_min_runners: +# labels: ["name", "namespace", "repository", "organization", "enterprise"] +# gha_max_runners: +# labels: ["name", "namespace", "repository", "organization", "enterprise"] +# gha_desired_runners: +# labels: ["name", "namespace", "repository", "organization", "enterprise"] +# gha_idle_runners: +# labels: ["name", "namespace", "repository", "organization", "enterprise"] +# histograms: +# gha_job_startup_duration_seconds: +# labels: +# ["repository", "organization", "enterprise", "job_name", "event_name","job_workflow_ref", "job_workflow_name", "job_workflow_target"] +# buckets: +# [ +# 0.01, +# 0.05, +# 0.1, +# 0.5, +# 1.0, +# 2.0, +# 3.0, +# 4.0, +# 5.0, +# 6.0, +# 7.0, +# 8.0, +# 9.0, +# 10.0, +# 12.0, +# 15.0, +# 18.0, +# 20.0, +# 25.0, +# 30.0, +# 40.0, +# 50.0, +# 60.0, +# 70.0, +# 80.0, +# 90.0, +# 100.0, +# 110.0, +# 120.0, +# 150.0, +# 180.0, +# 210.0, +# 240.0, +# 300.0, +# 360.0, +# 420.0, +# 480.0, +# 540.0, +# 600.0, +# 900.0, +# 1200.0, +# 1800.0, +# 2400.0, +# 3000.0, +# 3600.0, +# ] +# gha_job_execution_duration_seconds: +# labels: +# [ +# "repository", +# "organization", +# "enterprise", +# "job_name", +# "event_name", +# "job_result", +# "job_workflow_ref", +# "job_workflow_name", +# "job_workflow_target" +# ] +# buckets: +# [ +# 0.01, +# 0.05, +# 0.1, +# 0.5, +# 1.0, +# 2.0, +# 3.0, +# 4.0, +# 5.0, +# 6.0, +# 7.0, +# 8.0, +# 9.0, +# 10.0, +# 12.0, +# 15.0, +# 18.0, +# 20.0, +# 25.0, +# 30.0, +# 40.0, +# 50.0, +# 60.0, +# 70.0, +# 80.0, +# 90.0, +# 100.0, +# 110.0, +# 120.0, +# 150.0, +# 180.0, +# 210.0, +# 240.0, +# 300.0, +# 360.0, +# 420.0, +# 480.0, +# 540.0, +# 600.0, +# 900.0, +# 1200.0, +# 1800.0, +# 2400.0, +# 3000.0, +# 3600.0, +# ] diff --git a/charts/gha-runner-scale-set/.helmignore b/charts/gha-runner-scale-set/.helmignore index 0e8a0eb3..faeb926b 100644 --- a/charts/gha-runner-scale-set/.helmignore +++ b/charts/gha-runner-scale-set/.helmignore @@ -21,3 +21,4 @@ .idea/ *.tmproj .vscode/ +tests/ diff --git a/charts/gha-runner-scale-set/tests/autoscaling_runner_set_test.yaml b/charts/gha-runner-scale-set/tests/autoscaling_runner_set_test.yaml new file mode 100644 index 00000000..e5ce211e --- /dev/null +++ b/charts/gha-runner-scale-set/tests/autoscaling_runner_set_test.yaml @@ -0,0 +1,9 @@ +suite: "AutoscalingRunnerSet" +templates: + - autoscalingrunnserset.yaml +tests: + - it: should render base labels + set: + scaleset.name: "test" + auth.url: "https://github.com/org" + auth.githubToken: "gh_"