Compare commits

..

16 Commits

Author SHA1 Message Date
Nikola Jokic
ffdf9d05cf very much wip 2026-01-19 21:48:12 +01:00
Nikola Jokic
4056edbe9f wip 2026-01-19 19:00:58 +01:00
Nikola Jokic
d253b5b13a wip 2026-01-19 18:34:37 +01:00
Nikola Jokic
5b7873ee9a noperm 2026-01-19 18:34:37 +01:00
Nikola Jokic
832ac138b0 role/sa 2026-01-19 18:34:37 +01:00
Nikola Jokic
768ca41423 add manager role extra rules 2026-01-19 18:34:37 +01:00
Nikola Jokic
e4b0ce7b1d add tests 2026-01-19 18:34:37 +01:00
Nikola Jokic
0fc0b3a7a0 fix charts and start implementing githubsecret.yaml 2026-01-19 18:34:37 +01:00
Nikola Jokic
e8adcaf7c2 better coverage but still more to do 2026-01-19 18:34:37 +01:00
Nikola Jokic
c8f79e2b4a add more fields and tests 2026-01-19 18:34:37 +01:00
Nikola Jokic
a02c845761 adding tests checking auth 2026-01-19 18:34:37 +01:00
Nikola Jokic
7c5a443f94 Improve tests 2026-01-19 18:34:37 +01:00
Nikola Jokic
486374fe25 starting with tests 2026-01-19 18:34:37 +01:00
Nikola Jokic
cbf9e41a4a moving to chart tests 2026-01-19 18:34:37 +01:00
Nikola Jokic
1f379f4af1 slowly develop nicer helper functions 2026-01-19 18:34:37 +01:00
Nikola Jokic
7ccb042af9 rewriting the gha-runner-scale-set-chart 2026-01-19 18:34:37 +01:00
32 changed files with 3367 additions and 50 deletions

View File

@@ -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/

View File

@@ -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

View File

@@ -0,0 +1,437 @@
{{- 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 }}
{{/*
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 }}
{{/*
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 }}
{{/*
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 }}
{{/*
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 }}
{{/*
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 }}
{{/*
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 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 }}
{{/*
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 }}
{{/*
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-dind-container" -}}
{{- $dind := (.Values.runner.dind | default dict) -}}
name: {{ $dind.container.name | default "dind" | quote }}
image: {{ $dind.container.image | default "docker:dind" | quote }}
args:
{{- if $dind.container.args -}}
{{- toYaml $dind.container.args | indent 2 }}
{{- else }}
{{- toYaml (include "dind.default-args" .) | indent 2 }}
{{- end }}
env:
- name: DOCKER_GROUP_GID
value: "{{ $dind.dockerGroupGid | default "123" }}"
{{- if $dind.container.env -}}
{{ toYaml $dind.container.env | indent 2 }}
{{- end }}
securityContext:
{{- if $dind.container.securityContext -}}
{{ toYaml $dind.container.securityContext | indent 2 }}
{{- else }}
privileged: true
{{- end }}
restartPolicy: {{ $dind.restartPolicy | default "Always" | quote }}
startupProbe:
{{- if $dind.startupProbe -}}
{{ toYaml $dind.startupProbe | indent 2 }}
{{- else }}
{{- toYaml (include "dind.default-startup-probe" .) | indent 2 }}
{{- end }}
{{- end }}
{{- define "dind.default-startup-probe" -}}
exec:
command:
- docker
- info
initialDelaySeconds: 0
failureThreshold: 24
periodSeconds: 5
{{- end }}
{{- define "dind.default-args" -}}
- dockerd
- --host=unix:///var/run/docker.sock
- --group=$(DOCKER_GROUP_GID)
{{- 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 }}

View File

@@ -0,0 +1,132 @@
{{- $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 "") }}
{{- $usesKubernetesSecrets := or (not .Values.secretResolution) (eq .Values.secretResolution.type "kubernetes") }}
{{- $extraContainers := (index $runner "extraContainers" | default list) }}
{{- if not (kindIs "slice" $extraContainers) -}}
{{- fail ".Values.runner.extraContainers must be a list of container specifications" -}}
{{- end }}
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:
spec:
serviceAccountName: {{ include "no-permission-serviceaccount.name" . | quote }}
containers:
- {{ include "autoscaling-runner-set.template-runner-container" . | nindent 10 }}
{{- if eq $runnerMode "dind" -}}
- {{ include "autoscaling-runner-set.template-dind-container" . | nindent 10 }}
{{- end }}
{{- if $extraContainers }}
{{- range $extraContainers }}
- {{ toYaml . | nindent 10 }}
{{- end }}
{{- end }}

View File

@@ -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 -}}

View File

@@ -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 }}

View File

@@ -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 }}

View File

@@ -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 }}

View File

@@ -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 }}

View File

@@ -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 }}

View File

@@ -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 }}

View File

@@ -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"]

View File

@@ -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"

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"]

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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

View File

@@ -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"

View File

@@ -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"]

View File

@@ -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"

View File

@@ -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"

View File

@@ -0,0 +1,363 @@
## 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 required configuration for the selected mode
mode: "" # Available modes: "", "kubernetes", "dind"
pod:
metadata:
labels: {}
annotations: {}
spec: {}
workVolume:
type: ""
# 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"
container:
image: "docker:dind"
kubernetesMode:
serviceAccountName: ""
hookPath: "/home/runner/k8s/index.js"
requireJobContainer: true
extension: {}
extraInitContainers: []
extraContainers: []
extraVolumes: []
## 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,
# ]

View File

@@ -21,3 +21,4 @@
.idea/
*.tmproj
.vscode/
tests/

View File

@@ -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_"

33
go.mod
View File

@@ -3,7 +3,7 @@ module github.com/actions/actions-runner-controller
go 1.25.1
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.4.0
github.com/bradleyfalzon/ghinstallation/v2 v2.17.0
@@ -16,27 +16,27 @@ require (
github.com/google/uuid v1.6.0
github.com/gorilla/mux v1.8.1
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79
github.com/gruntwork-io/terratest v0.55.0
github.com/gruntwork-io/terratest v0.54.0
github.com/hashicorp/go-retryablehttp v0.7.8
github.com/kelseyhightower/envconfig v1.4.0
github.com/onsi/ginkgo v1.16.5
github.com/onsi/ginkgo/v2 v2.27.5
github.com/onsi/gomega v1.39.0
github.com/onsi/ginkgo/v2 v2.27.3
github.com/onsi/gomega v1.38.3
github.com/prometheus/client_golang v1.23.2
github.com/stretchr/testify v1.11.1
github.com/teambition/rrule-go v1.8.2
go.uber.org/multierr v1.11.0
go.uber.org/zap v1.27.1
golang.org/x/net v0.49.0
golang.org/x/net v0.48.0
golang.org/x/oauth2 v0.34.0
golang.org/x/sync v0.19.0
gomodules.xyz/jsonpatch/v2 v2.5.0
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.35.0
k8s.io/apimachinery v0.35.0
k8s.io/client-go v0.35.0
k8s.io/api v0.34.3
k8s.io/apimachinery v0.34.3
k8s.io/client-go v0.34.3
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4
sigs.k8s.io/controller-runtime v0.23.0
sigs.k8s.io/controller-runtime v0.22.4
sigs.k8s.io/yaml v1.6.0
)
@@ -111,6 +111,7 @@ require (
github.com/go-openapi/swag/yamlutils v0.25.1 // indirect
github.com/go-sql-driver/mysql v1.9.3 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
github.com/gonvenience/bunt v1.4.2 // indirect
github.com/gonvenience/idem v0.0.2 // indirect
@@ -164,19 +165,19 @@ require (
github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.47.0 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/exp v0.0.0-20251002181428-27f1f14c8bb9 // indirect
golang.org/x/mod v0.31.0 // indirect
golang.org/x/sys v0.40.0 // indirect
golang.org/x/term v0.39.0 // indirect
golang.org/x/text v0.33.0 // indirect
golang.org/x/mod v0.30.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/term v0.38.0 // indirect
golang.org/x/text v0.32.0 // indirect
golang.org/x/time v0.13.0 // indirect
golang.org/x/tools v0.40.0 // indirect
golang.org/x/tools v0.39.0 // indirect
google.golang.org/protobuf v1.36.10 // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.35.0 // indirect
k8s.io/apiextensions-apiserver v0.34.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect

78
go.sum
View File

@@ -1,8 +1,8 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/Azure/azure-sdk-for-go v51.0.0+incompatible h1:p7blnyJSjJqf5jflHbSGhIhEpXIgIFmYZNg5uwqweso=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
@@ -179,6 +179,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
@@ -231,8 +233,8 @@ github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5T
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/gruntwork-io/go-commons v0.17.2 h1:14dsCJ7M5Vv2X3BIPKeG9Kdy6vTMGhM8L4WZazxfTuY=
github.com/gruntwork-io/go-commons v0.17.2/go.mod h1:zs7Q2AbUKuTarBPy19CIxJVUX/rBamfW8IwuWKniWkE=
github.com/gruntwork-io/terratest v0.55.0 h1:NgG6lm2dArdQ3KcOofw6PTfVRK1Flt7L3NNhFSBo72A=
github.com/gruntwork-io/terratest v0.55.0/go.mod h1:OE0Jsc8Wn5kw/QySLbBd53g9Gt+xfDyDKChwRHwkKvI=
github.com/gruntwork-io/terratest v0.54.0 h1:JOVATYDpU0NAPbEkgYUP50BR2m45UGiR4dbs20sKzck=
github.com/gruntwork-io/terratest v0.54.0/go.mod h1:QvwQWZMTJmJB4E0d1Uc18quQm7+X53liKKp+fJSuaKA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@@ -263,6 +265,8 @@ github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dv
github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -312,12 +316,12 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.27.5 h1:ZeVgZMx2PDMdJm/+w5fE/OyG6ILo1Y3e+QX4zSR0zTE=
github.com/onsi/ginkgo/v2 v2.27.5/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
github.com/onsi/ginkgo/v2 v2.27.3 h1:ICsZJ8JoYafeXFFlFAG75a7CxMsJHwgKwtO+82SE9L8=
github.com/onsi/ginkgo/v2 v2.27.3/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.39.0 h1:y2ROC3hKFmQZJNFeGAMeHZKkjBL65mIZcvrLQBF9k6Q=
github.com/onsi/gomega v1.39.0/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=
github.com/onsi/gomega v1.38.3 h1:eTX+W6dobAYfFeGC2PV6RwXRu/MyT+cQguijutvkpSM=
github.com/onsi/gomega v1.38.3/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -335,8 +339,8 @@ github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9Z
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
@@ -372,6 +376,7 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg=
github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
@@ -386,24 +391,27 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/exp v0.0.0-20251002181428-27f1f14c8bb9 h1:TQwNpfvNkxAVlItJf6Cr5JTsVZoC/Sj7K3OZv2Pc14A=
golang.org/x/exp v0.0.0-20251002181428-27f1f14c8bb9/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
@@ -418,21 +426,23 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI=
golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -465,22 +475,22 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.35.0 h1:iBAU5LTyBI9vw3L5glmat1njFK34srdLmktWwLTprlY=
k8s.io/api v0.35.0/go.mod h1:AQ0SNTzm4ZAczM03QH42c7l3bih1TbAXYo0DkF8ktnA=
k8s.io/apiextensions-apiserver v0.35.0 h1:3xHk2rTOdWXXJM+RDQZJvdx0yEOgC0FgQ1PlJatA5T4=
k8s.io/apiextensions-apiserver v0.35.0/go.mod h1:E1Ahk9SADaLQ4qtzYFkwUqusXTcaV2uw3l14aqpL2LU=
k8s.io/apimachinery v0.35.0 h1:Z2L3IHvPVv/MJ7xRxHEtk6GoJElaAqDCCU0S6ncYok8=
k8s.io/apimachinery v0.35.0/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
k8s.io/client-go v0.35.0 h1:IAW0ifFbfQQwQmga0UdoH0yvdqrbwMdq9vIFEhRpxBE=
k8s.io/client-go v0.35.0/go.mod h1:q2E5AAyqcbeLGPdoRB+Nxe3KYTfPce1Dnu1myQdqz9o=
k8s.io/api v0.34.3 h1:D12sTP257/jSH2vHV2EDYrb16bS7ULlHpdNdNhEw2S4=
k8s.io/api v0.34.3/go.mod h1:PyVQBF886Q5RSQZOim7DybQjAbVs8g7gwJNhGtY5MBk=
k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI=
k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc=
k8s.io/apimachinery v0.34.3 h1:/TB+SFEiQvN9HPldtlWOTp0hWbJ+fjU+wkxysf/aQnE=
k8s.io/apimachinery v0.34.3/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A=
k8s.io/client-go v0.34.3/go.mod h1:OxxeYagaP9Kdf78UrKLa3YZixMCfP6bgPwPwNBQBzpM=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE=
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck=
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/controller-runtime v0.23.0 h1:Ubi7klJWiwEWqDY+odSVZiFA0aDSevOCXpa38yCSYu8=
sigs.k8s.io/controller-runtime v0.23.0/go.mod h1:DBOIr9NsprUqCZ1ZhsuJ0wAnQSIxY/C6VjZbmLgw0j0=
sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A=
sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=