From 85b62cf5068338df586b4a6c1d29f52b637f4506 Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Mon, 16 Mar 2026 13:37:01 +0100 Subject: [PATCH] Scale set add multi-label support --- .../v1alpha1/autoscalingrunnerset_types.go | 3 ++ .../v1alpha1/zz_generated.deepcopy.go | 5 +++ ...ions.github.com_autoscalingrunnersets.yaml | 4 +++ ...ions.github.com_autoscalingrunnersets.yaml | 4 +++ .../templates/autoscalingrunnserset.yaml | 4 +++ ...ling_runner_set_scale_set_labels_test.yaml | 24 ++++++++++++++ .../values.yaml | 4 +++ .../templates/autoscalingrunnerset.yaml | 4 +++ .../tests/template_test.go | 31 +++++++++++++++++++ charts/gha-runner-scale-set/values.yaml | 2 ++ ...ions.github.com_autoscalingrunnersets.yaml | 4 +++ .../autoscalingrunnerset_controller.go | 30 ++++++++++++++---- 12 files changed, 113 insertions(+), 6 deletions(-) create mode 100644 charts/gha-runner-scale-set-experimental/tests/autoscaling_runner_set_scale_set_labels_test.yaml diff --git a/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go b/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go index 90049d27..53384539 100644 --- a/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go +++ b/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go @@ -66,6 +66,9 @@ type AutoscalingRunnerSetSpec struct { // +optional RunnerScaleSetName string `json:"runnerScaleSetName,omitempty"` + // +optional + RunnerScaleSetLabels []string `json:"runnerScaleSetLabels,omitempty"` + // +optional Proxy *ProxyConfig `json:"proxy,omitempty"` diff --git a/apis/actions.github.com/v1alpha1/zz_generated.deepcopy.go b/apis/actions.github.com/v1alpha1/zz_generated.deepcopy.go index 232d6f86..df2dabc8 100644 --- a/apis/actions.github.com/v1alpha1/zz_generated.deepcopy.go +++ b/apis/actions.github.com/v1alpha1/zz_generated.deepcopy.go @@ -227,6 +227,11 @@ func (in *AutoscalingRunnerSetList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AutoscalingRunnerSetSpec) DeepCopyInto(out *AutoscalingRunnerSetSpec) { *out = *in + if in.RunnerScaleSetLabels != nil { + in, out := &in.RunnerScaleSetLabels, &out.RunnerScaleSetLabels + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.Proxy != nil { in, out := &in.Proxy, &out.Proxy *out = new(ProxyConfig) diff --git a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_autoscalingrunnersets.yaml b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_autoscalingrunnersets.yaml index 1b18467f..17adca1e 100644 --- a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_autoscalingrunnersets.yaml +++ b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_autoscalingrunnersets.yaml @@ -8385,6 +8385,10 @@ spec: type: object runnerGroup: type: string + runnerScaleSetLabels: + items: + type: string + type: array runnerScaleSetName: type: string template: diff --git a/charts/gha-runner-scale-set-controller/crds/actions.github.com_autoscalingrunnersets.yaml b/charts/gha-runner-scale-set-controller/crds/actions.github.com_autoscalingrunnersets.yaml index 1b18467f..17adca1e 100644 --- a/charts/gha-runner-scale-set-controller/crds/actions.github.com_autoscalingrunnersets.yaml +++ b/charts/gha-runner-scale-set-controller/crds/actions.github.com_autoscalingrunnersets.yaml @@ -8385,6 +8385,10 @@ spec: type: object runnerGroup: type: string + runnerScaleSetLabels: + items: + type: string + type: array runnerScaleSetName: type: string template: diff --git a/charts/gha-runner-scale-set-experimental/templates/autoscalingrunnserset.yaml b/charts/gha-runner-scale-set-experimental/templates/autoscalingrunnserset.yaml index a97caf4d..f0d2c8a4 100644 --- a/charts/gha-runner-scale-set-experimental/templates/autoscalingrunnserset.yaml +++ b/charts/gha-runner-scale-set-experimental/templates/autoscalingrunnserset.yaml @@ -83,6 +83,10 @@ spec: githubConfigSecret: {{ include "github-secret.name" . | quote }} runnerGroup: {{ .Values.scaleset.runnerGroup | quote }} runnerScaleSetName: {{ .Values.scaleset.name | quote }} + {{- if and .Values.scaleset.labels (kindIs "slice" .Values.scaleset.labels) }} + runnerScaleSetLabels: + {{- toYaml .Values.scaleset.labels | nindent 4 }} + {{- end }} {{- if .Values.githubServerTLS }} githubServerTLS: diff --git a/charts/gha-runner-scale-set-experimental/tests/autoscaling_runner_set_scale_set_labels_test.yaml b/charts/gha-runner-scale-set-experimental/tests/autoscaling_runner_set_scale_set_labels_test.yaml new file mode 100644 index 00000000..6176694b --- /dev/null +++ b/charts/gha-runner-scale-set-experimental/tests/autoscaling_runner_set_scale_set_labels_test.yaml @@ -0,0 +1,24 @@ +suite: "AutoscalingRunnerSet scale set labels" +templates: + - autoscalingrunnserset.yaml +tests: + - it: should apply scaleset labels slice as runnerScaleSetLabels + set: + scaleset.name: "test" + scaleset.labels: + - "linux" + - "x64" + 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.runnerScaleSetLabels[0] + value: "linux" + - equal: + path: spec.runnerScaleSetLabels[1] + value: "x64" \ No newline at end of file diff --git a/charts/gha-runner-scale-set-experimental/values.yaml b/charts/gha-runner-scale-set-experimental/values.yaml index eced4064..19444b59 100644 --- a/charts/gha-runner-scale-set-experimental/values.yaml +++ b/charts/gha-runner-scale-set-experimental/values.yaml @@ -5,6 +5,10 @@ scaleset: # Name of the scaleset name: "" runnerGroup: "default" + # Labels are optional list of strings that will be applied to the scaleset + # allowing scaleset to be selected by the listener based on the labels specified in the workflow. + # https://docs.github.com/en/actions/how-tos/manage-runners/self-hosted-runners/apply-labels + labels: [] ## 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 diff --git a/charts/gha-runner-scale-set/templates/autoscalingrunnerset.yaml b/charts/gha-runner-scale-set/templates/autoscalingrunnerset.yaml index db6ba9d0..0771e9c9 100644 --- a/charts/gha-runner-scale-set/templates/autoscalingrunnerset.yaml +++ b/charts/gha-runner-scale-set/templates/autoscalingrunnerset.yaml @@ -74,6 +74,10 @@ spec: {{- with .Values.runnerScaleSetName }} runnerScaleSetName: {{ . }} {{- end }} + {{- if and .Values.scaleSetLabels (kindIs "slice" .Values.scaleSetLabels) }} + runnerScaleSetLabels: + {{- toYaml .Values.scaleSetLabels | nindent 4 }} + {{- end }} {{- if .Values.githubServerTLS }} githubServerTLS: diff --git a/charts/gha-runner-scale-set/tests/template_test.go b/charts/gha-runner-scale-set/tests/template_test.go index 7a657475..9d6c2e92 100644 --- a/charts/gha-runner-scale-set/tests/template_test.go +++ b/charts/gha-runner-scale-set/tests/template_test.go @@ -474,6 +474,37 @@ func TestTemplateRenderedAutoScalingRunnerSet_RunnerScaleSetName(t *testing.T) { assert.Equal(t, "ghcr.io/actions/actions-runner:latest", ars.Spec.Template.Spec.Containers[0].Image) } +func TestTemplateRenderedAutoScalingRunnerSet_ScaleSetLabels(t *testing.T) { + t.Parallel() + + // Path to the helm chart we will test + helmChartPath, err := filepath.Abs("../../gha-runner-scale-set") + require.NoError(t, err) + + releaseName := "test-runners" + namespaceName := "test-" + strings.ToLower(random.UniqueId()) + + options := &helm.Options{ + Logger: logger.Discard, + SetValues: map[string]string{ + "githubConfigUrl": "https://github.com/actions", + "githubConfigSecret.github_token": "gh_token12345", + "scaleSetLabels[0]": "linux", + "scaleSetLabels[1]": "x64", + "controllerServiceAccount.name": "arc", + "controllerServiceAccount.namespace": "arc-system", + }, + KubectlOptions: k8s.NewKubectlOptions("", "", namespaceName), + } + + output := helm.RenderTemplate(t, options, helmChartPath, releaseName, []string{"templates/autoscalingrunnerset.yaml"}) + + var ars v1alpha1.AutoscalingRunnerSet + helm.UnmarshalK8SYaml(t, output, &ars) + + assert.Equal(t, []string{"linux", "x64"}, ars.Spec.RunnerScaleSetLabels) +} + func TestTemplateRenderedAutoScalingRunnerSet_ProvideMetadata(t *testing.T) { t.Parallel() diff --git a/charts/gha-runner-scale-set/values.yaml b/charts/gha-runner-scale-set/values.yaml index 8a9b64e9..b8d754f3 100644 --- a/charts/gha-runner-scale-set/values.yaml +++ b/charts/gha-runner-scale-set/values.yaml @@ -2,6 +2,8 @@ ## ex: https://github.com/myorg/myrepo or https://github.com/myorg or https://github.com/enterprises/myenterprise githubConfigUrl: "" +scaleSetLabels: [] + ## githubConfigSecret is the k8s secret information to use when authenticating via the GitHub API. ## You can choose to supply: ## A) a PAT token, diff --git a/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml b/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml index 1b18467f..17adca1e 100644 --- a/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml +++ b/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml @@ -8385,6 +8385,10 @@ spec: type: object runnerGroup: type: string + runnerScaleSetLabels: + items: + type: string + type: array runnerScaleSetName: type: string template: diff --git a/controllers/actions.github.com/autoscalingrunnerset_controller.go b/controllers/actions.github.com/autoscalingrunnerset_controller.go index c2d48d16..9494fd41 100644 --- a/controllers/actions.github.com/autoscalingrunnerset_controller.go +++ b/controllers/actions.github.com/autoscalingrunnerset_controller.go @@ -424,17 +424,35 @@ func (r *AutoscalingRunnerSetReconciler) createRunnerScaleSet(ctx context.Contex } if runnerScaleSet == nil { + labels := []scaleset.Label{ + { + Name: autoscalingRunnerSet.Spec.RunnerScaleSetName, + Type: "System", + }, + } + + if labelCount := len(autoscalingRunnerSet.Spec.RunnerScaleSetLabels); labelCount > 0 { + unique := make(map[string]bool, labelCount+1) + unique[autoscalingRunnerSet.Spec.RunnerScaleSetName] = true + + for _, label := range autoscalingRunnerSet.Spec.RunnerScaleSetLabels { + if _, exists := unique[label]; exists { + logger.Info("Duplicate label found. Skipping adding duplicate label to runner scale set", "label", label) + continue + } + labels = append(labels, scaleset.Label{ + Name: label, + Type: "System", + }) + unique[label] = true + } + } runnerScaleSet, err = actionsClient.CreateRunnerScaleSet( ctx, &scaleset.RunnerScaleSet{ Name: autoscalingRunnerSet.Spec.RunnerScaleSetName, RunnerGroupID: runnerGroupID, - Labels: []scaleset.Label{ - { - Name: autoscalingRunnerSet.Spec.RunnerScaleSetName, - Type: "System", - }, - }, + Labels: labels, RunnerSetting: scaleset.RunnerSetting{ DisableUpdate: true, },