From 1435f1d366028d932661ea5515aca1c1cb1ebb47 Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Fri, 12 Jun 2026 17:10:10 +0200 Subject: [PATCH 01/12] Fix ephemeral runner set status update --- .../v1alpha1/autoscalingrunnerset_types.go | 1 + apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go | 4 ++++ .../crds/actions.github.com_autoscalingrunnersets.yaml | 1 + .../crds/actions.github.com_ephemeralrunnersets.yaml | 4 ++++ .../crds/actions.github.com_autoscalingrunnersets.yaml | 1 + .../crds/actions.github.com_ephemeralrunnersets.yaml | 4 ++++ .../templates/_mode_kubernetes.tpl | 5 ++++- .../crd/bases/actions.github.com_autoscalingrunnersets.yaml | 1 + config/crd/bases/actions.github.com_ephemeralrunnersets.yaml | 4 ++++ .../actions.github.com/ephemeralrunnerset_controller.go | 3 +-- 10 files changed, 25 insertions(+), 3 deletions(-) diff --git a/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go b/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go index 24ccc8e328..09f014888f 100644 --- a/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go +++ b/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go @@ -315,6 +315,7 @@ type HistogramMetric struct { // AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet type AutoscalingRunnerSetStatus struct { // +optional + // +kubebuilder:validation:Minimum=0 CurrentRunners int `json:"currentRunners"` // +optional diff --git a/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go b/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go index bfcd424d84..9bdd8de368 100644 --- a/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go +++ b/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go @@ -38,12 +38,16 @@ type EphemeralRunnerSetSpec struct { // EphemeralRunnerSetStatus defines the observed state of EphemeralRunnerSet type EphemeralRunnerSetStatus struct { // CurrentReplicas is the number of currently running EphemeralRunner resources being managed by this EphemeralRunnerSet. + // +kubebuilder:validation:Minimum=0 CurrentReplicas int `json:"currentReplicas"` // +optional + // +kubebuilder:validation:Minimum=0 PendingEphemeralRunners int `json:"pendingEphemeralRunners"` // +optional + // +kubebuilder:validation:Minimum=0 RunningEphemeralRunners int `json:"runningEphemeralRunners"` // +optional + // +kubebuilder:validation:Minimum=0 FailedEphemeralRunners int `json:"failedEphemeralRunners"` // +optional Phase EphemeralRunnerSetPhase `json:"phase"` 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 82001c24f4..d02ff80e07 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 @@ -16544,6 +16544,7 @@ spec: description: AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet properties: currentRunners: + minimum: 0 type: integer failedEphemeralRunners: type: integer diff --git a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunnersets.yaml b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunnersets.yaml index ae035cfbf7..b7a8d5e954 100644 --- a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunnersets.yaml +++ b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunnersets.yaml @@ -8315,15 +8315,19 @@ spec: properties: currentReplicas: description: CurrentReplicas is the number of currently running EphemeralRunner resources being managed by this EphemeralRunnerSet. + minimum: 0 type: integer failedEphemeralRunners: + minimum: 0 type: integer pendingEphemeralRunners: + minimum: 0 type: integer phase: description: EphemeralRunnerSetPhase is the phase of the ephemeral runner set resource type: string runningEphemeralRunners: + minimum: 0 type: integer required: - currentReplicas 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 82001c24f4..d02ff80e07 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 @@ -16544,6 +16544,7 @@ spec: description: AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet properties: currentRunners: + minimum: 0 type: integer failedEphemeralRunners: type: integer diff --git a/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunnersets.yaml b/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunnersets.yaml index ae035cfbf7..b7a8d5e954 100644 --- a/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunnersets.yaml +++ b/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunnersets.yaml @@ -8315,15 +8315,19 @@ spec: properties: currentReplicas: description: CurrentReplicas is the number of currently running EphemeralRunner resources being managed by this EphemeralRunnerSet. + minimum: 0 type: integer failedEphemeralRunners: + minimum: 0 type: integer pendingEphemeralRunners: + minimum: 0 type: integer phase: description: EphemeralRunnerSetPhase is the phase of the ephemeral runner set resource type: string runningEphemeralRunners: + minimum: 0 type: integer required: - currentReplicas diff --git a/charts/gha-runner-scale-set-experimental/templates/_mode_kubernetes.tpl b/charts/gha-runner-scale-set-experimental/templates/_mode_kubernetes.tpl index 6589d01d1c..90800e80d3 100644 --- a/charts/gha-runner-scale-set-experimental/templates/_mode_kubernetes.tpl +++ b/charts/gha-runner-scale-set-experimental/templates/_mode_kubernetes.tpl @@ -82,7 +82,10 @@ volumeMounts: subPath: extension readOnly: true {{- end }} - {{ include "githubServerTLS.volumeMountItem" (dict "root" $ "existingVolumeMounts" (list)) | nindent 2 }} + {{- with .Values.runner.container.volumeMounts }} + {{- toYaml . | nindent 2 }} + {{- end }} + {{ include "githubServerTLS.volumeMountItem" (dict "root" $ "existingVolumeMounts" (.Values.runner.container.volumeMounts | default list)) | nindent 2 }} {{- end }} {{- define "runner-mode-kubernetes.pod-volumes" -}} diff --git a/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml b/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml index 82001c24f4..d02ff80e07 100644 --- a/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml +++ b/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml @@ -16544,6 +16544,7 @@ spec: description: AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet properties: currentRunners: + minimum: 0 type: integer failedEphemeralRunners: type: integer diff --git a/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml b/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml index ae035cfbf7..b7a8d5e954 100644 --- a/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml +++ b/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml @@ -8315,15 +8315,19 @@ spec: properties: currentReplicas: description: CurrentReplicas is the number of currently running EphemeralRunner resources being managed by this EphemeralRunnerSet. + minimum: 0 type: integer failedEphemeralRunners: + minimum: 0 type: integer pendingEphemeralRunners: + minimum: 0 type: integer phase: description: EphemeralRunnerSetPhase is the phase of the ephemeral runner set resource type: string runningEphemeralRunners: + minimum: 0 type: integer required: - currentReplicas diff --git a/controllers/actions.github.com/ephemeralrunnerset_controller.go b/controllers/actions.github.com/ephemeralrunnerset_controller.go index 381c9d29de..f8b5f490f7 100644 --- a/controllers/actions.github.com/ephemeralrunnerset_controller.go +++ b/controllers/actions.github.com/ephemeralrunnerset_controller.go @@ -271,7 +271,6 @@ func (r *EphemeralRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl.R } func (r *EphemeralRunnerSetReconciler) updateStatus(ctx context.Context, ephemeralRunnerSet *v1alpha1.EphemeralRunnerSet, state *ephemeralRunnersByState, log logr.Logger) error { - original := ephemeralRunnerSet.DeepCopy() total := state.scaleTotal() var phase v1alpha1.EphemeralRunnerSetPhase switch { @@ -293,7 +292,7 @@ func (r *EphemeralRunnerSetReconciler) updateStatus(ctx context.Context, ephemer // Update the status if needed. if ephemeralRunnerSet.Status != desiredStatus { ephemeralRunnerSet.Status = desiredStatus - if err := r.Status().Patch(ctx, ephemeralRunnerSet, client.MergeFrom(original)); err != nil { + if err := r.Status().Update(ctx, ephemeralRunnerSet); err != nil { log.Error(err, "Failed to update EphemeralRunnerSet status") return err } From 0e921def3b672d60990ea14f8aae2f35a6d81b30 Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Sat, 13 Jun 2026 01:13:55 +0200 Subject: [PATCH 02/12] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../templates/_mode_kubernetes.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/gha-runner-scale-set-experimental/templates/_mode_kubernetes.tpl b/charts/gha-runner-scale-set-experimental/templates/_mode_kubernetes.tpl index 90800e80d3..672c8495d4 100644 --- a/charts/gha-runner-scale-set-experimental/templates/_mode_kubernetes.tpl +++ b/charts/gha-runner-scale-set-experimental/templates/_mode_kubernetes.tpl @@ -85,7 +85,7 @@ volumeMounts: {{- with .Values.runner.container.volumeMounts }} {{- toYaml . | nindent 2 }} {{- end }} - {{ include "githubServerTLS.volumeMountItem" (dict "root" $ "existingVolumeMounts" (.Values.runner.container.volumeMounts | default list)) | nindent 2 }} + {{ include "githubServerTLS.volumeMountItem" (dict "root" $ "existingVolumeMounts" (.Values.runner.container.volumeMounts | default (list))) | nindent 2 }} {{- end }} {{- define "runner-mode-kubernetes.pod-volumes" -}} From 6089fcbb3fa9b8d4444ae42462f6ed4d48d11be1 Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Mon, 15 Jun 2026 10:41:30 +0200 Subject: [PATCH 03/12] Renam Url to URL --- .../v1alpha1/autoscalingrunnerset_types.go | 13 ++++++++----- .../v1alpha1/proxy_config_test.go | 8 ++++---- .../actions.github.com_autoscalingrunnersets.yaml | 3 +++ .../actions.github.com_autoscalingrunnersets.yaml | 3 +++ charts/gha-runner-scale-set/tests/template_test.go | 4 ++-- .../actions.github.com_autoscalingrunnersets.yaml | 3 +++ .../autoscalinglistener_controller_test.go | 4 ++-- .../autoscalingrunnerset_controller_test.go | 4 ++-- .../ephemeralrunner_controller_test.go | 6 +++--- .../ephemeralrunnerset_controller_test.go | 6 +++--- .../secretresolver/secret_resolver.go | 8 ++++---- 11 files changed, 37 insertions(+), 25 deletions(-) diff --git a/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go b/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go index 09f014888f..69ad40c92e 100644 --- a/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go +++ b/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go @@ -179,9 +179,9 @@ func (c *ProxyConfig) ToHTTPProxyConfig(secretFetcher func(string) (*corev1.Secr } if c.HTTP != nil { - u, err := url.Parse(c.HTTP.Url) + u, err := url.Parse(c.HTTP.URL) if err != nil { - return nil, fmt.Errorf("failed to parse proxy http url %q: %w", c.HTTP.Url, err) + return nil, fmt.Errorf("failed to parse proxy http url %q: %w", c.HTTP.URL, err) } if c.HTTP.CredentialSecretRef != "" { @@ -204,9 +204,9 @@ func (c *ProxyConfig) ToHTTPProxyConfig(secretFetcher func(string) (*corev1.Secr } if c.HTTPS != nil { - u, err := url.Parse(c.HTTPS.Url) + u, err := url.Parse(c.HTTPS.URL) if err != nil { - return nil, fmt.Errorf("failed to parse proxy https url %q: %w", c.HTTPS.Url, err) + return nil, fmt.Errorf("failed to parse proxy https url %q: %w", c.HTTPS.URL, err) } if c.HTTPS.CredentialSecretRef != "" { @@ -260,7 +260,7 @@ func (c *ProxyConfig) ProxyFunc(secretFetcher func(string) (*corev1.Secret, erro type ProxyServerConfig struct { // Required - Url string `json:"url,omitempty"` + URL string `json:"url,omitempty"` // +optional CredentialSecretRef string `json:"credentialSecretRef,omitempty"` @@ -324,10 +324,13 @@ type AutoscalingRunnerSetStatus struct { // EphemeralRunner counts separated by the stage ephemeral runners are in, taken from the EphemeralRunnerSet // +optional + // +kubebuilder:validation:Minimum=0 PendingEphemeralRunners int `json:"pendingEphemeralRunners"` // +optional + // +kubebuilder:validation:Minimum=0 RunningEphemeralRunners int `json:"runningEphemeralRunners"` // +optional + // +kubebuilder:validation:Minimum=0 FailedEphemeralRunners int `json:"failedEphemeralRunners"` } diff --git a/apis/actions.github.com/v1alpha1/proxy_config_test.go b/apis/actions.github.com/v1alpha1/proxy_config_test.go index 9291cde4e0..0357e5e268 100644 --- a/apis/actions.github.com/v1alpha1/proxy_config_test.go +++ b/apis/actions.github.com/v1alpha1/proxy_config_test.go @@ -14,11 +14,11 @@ import ( func TestProxyConfig_ToSecret(t *testing.T) { config := &v1alpha1.ProxyConfig{ HTTP: &v1alpha1.ProxyServerConfig{ - Url: "http://proxy.example.com:8080", + URL: "http://proxy.example.com:8080", CredentialSecretRef: "my-secret", }, HTTPS: &v1alpha1.ProxyServerConfig{ - Url: "https://proxy.example.com:8080", + URL: "https://proxy.example.com:8080", CredentialSecretRef: "my-secret", }, NoProxy: []string{ @@ -48,11 +48,11 @@ func TestProxyConfig_ToSecret(t *testing.T) { func TestProxyConfig_ProxyFunc(t *testing.T) { config := &v1alpha1.ProxyConfig{ HTTP: &v1alpha1.ProxyServerConfig{ - Url: "http://proxy.example.com:8080", + URL: "http://proxy.example.com:8080", CredentialSecretRef: "my-secret", }, HTTPS: &v1alpha1.ProxyServerConfig{ - Url: "https://proxy.example.com:8080", + URL: "https://proxy.example.com:8080", CredentialSecretRef: "my-secret", }, NoProxy: []string{ 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 d02ff80e07..20f126255e 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 @@ -16547,12 +16547,15 @@ spec: minimum: 0 type: integer failedEphemeralRunners: + minimum: 0 type: integer pendingEphemeralRunners: + minimum: 0 type: integer phase: type: string runningEphemeralRunners: + minimum: 0 type: integer type: object type: object 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 d02ff80e07..20f126255e 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 @@ -16547,12 +16547,15 @@ spec: minimum: 0 type: integer failedEphemeralRunners: + minimum: 0 type: integer pendingEphemeralRunners: + minimum: 0 type: integer phase: type: string runningEphemeralRunners: + minimum: 0 type: integer type: object type: object diff --git a/charts/gha-runner-scale-set/tests/template_test.go b/charts/gha-runner-scale-set/tests/template_test.go index ba67ea90ec..10a3ddfdfe 100644 --- a/charts/gha-runner-scale-set/tests/template_test.go +++ b/charts/gha-runner-scale-set/tests/template_test.go @@ -1364,11 +1364,11 @@ func TestTemplateRenderedWithProxy(t *testing.T) { require.NotNil(t, ars.Spec.Proxy) require.NotNil(t, ars.Spec.Proxy.HTTP) - assert.Equal(t, "http://proxy.example.com", ars.Spec.Proxy.HTTP.Url) + assert.Equal(t, "http://proxy.example.com", ars.Spec.Proxy.HTTP.URL) assert.Equal(t, "http-secret", ars.Spec.Proxy.HTTP.CredentialSecretRef) require.NotNil(t, ars.Spec.Proxy.HTTPS) - assert.Equal(t, "https://proxy.example.com", ars.Spec.Proxy.HTTPS.Url) + assert.Equal(t, "https://proxy.example.com", ars.Spec.Proxy.HTTPS.URL) assert.Equal(t, "https-secret", ars.Spec.Proxy.HTTPS.CredentialSecretRef) require.NotNil(t, ars.Spec.Proxy.NoProxy) diff --git a/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml b/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml index d02ff80e07..20f126255e 100644 --- a/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml +++ b/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml @@ -16547,12 +16547,15 @@ spec: minimum: 0 type: integer failedEphemeralRunners: + minimum: 0 type: integer pendingEphemeralRunners: + minimum: 0 type: integer phase: type: string runningEphemeralRunners: + minimum: 0 type: integer type: object type: object diff --git a/controllers/actions.github.com/autoscalinglistener_controller_test.go b/controllers/actions.github.com/autoscalinglistener_controller_test.go index d48d613e6c..8ec0ebc27c 100644 --- a/controllers/actions.github.com/autoscalinglistener_controller_test.go +++ b/controllers/actions.github.com/autoscalinglistener_controller_test.go @@ -954,11 +954,11 @@ var _ = Describe("Test AutoScalingListener controller with proxy", func() { proxy := &v1alpha1.ProxyConfig{ HTTP: &v1alpha1.ProxyServerConfig{ - Url: "http://localhost:8080", + URL: "http://localhost:8080", CredentialSecretRef: "proxy-credentials", }, HTTPS: &v1alpha1.ProxyServerConfig{ - Url: "https://localhost:8443", + URL: "https://localhost:8443", CredentialSecretRef: "proxy-credentials", }, NoProxy: []string{ diff --git a/controllers/actions.github.com/autoscalingrunnerset_controller_test.go b/controllers/actions.github.com/autoscalingrunnerset_controller_test.go index 55ebe5c6ae..a44d1cb9b2 100644 --- a/controllers/actions.github.com/autoscalingrunnerset_controller_test.go +++ b/controllers/actions.github.com/autoscalingrunnerset_controller_test.go @@ -1353,7 +1353,7 @@ var _ = Describe("Test client optional configuration", Ordered, func() { RunnerGroup: "testgroup", Proxy: &v1alpha1.ProxyConfig{ HTTP: &v1alpha1.ProxyServerConfig{ - Url: proxy.URL, + URL: proxy.URL, }, }, Template: corev1.PodTemplateSpec{ @@ -1431,7 +1431,7 @@ var _ = Describe("Test client optional configuration", Ordered, func() { RunnerGroup: "testgroup", Proxy: &v1alpha1.ProxyConfig{ HTTP: &v1alpha1.ProxyServerConfig{ - Url: "http://test:password@" + proxy.Listener.Addr().String(), + URL: "http://test:password@" + proxy.Listener.Addr().String(), CredentialSecretRef: "proxy-credentials", }, }, diff --git a/controllers/actions.github.com/ephemeralrunner_controller_test.go b/controllers/actions.github.com/ephemeralrunner_controller_test.go index 80c27134a7..74aafb71a1 100644 --- a/controllers/actions.github.com/ephemeralrunner_controller_test.go +++ b/controllers/actions.github.com/ephemeralrunner_controller_test.go @@ -1367,7 +1367,7 @@ var _ = Describe("EphemeralRunner", func() { ephemeralRunner.Spec.GitHubConfigURL = "http://example.com/org/repo" ephemeralRunner.Spec.Proxy = &v1alpha1.ProxyConfig{ HTTP: &v1alpha1.ProxyServerConfig{ - Url: proxy.URL, + URL: proxy.URL, CredentialSecretRef: "proxy-credentials", }, } @@ -1388,10 +1388,10 @@ var _ = Describe("EphemeralRunner", func() { ephemeralRunner := newExampleRunner("test-runner", autoScalingNS.Name, configSecret.Name) ephemeralRunner.Spec.Proxy = &v1alpha1.ProxyConfig{ HTTP: &v1alpha1.ProxyServerConfig{ - Url: "http://proxy.example.com:8080", + URL: "http://proxy.example.com:8080", }, HTTPS: &v1alpha1.ProxyServerConfig{ - Url: "http://proxy.example.com:8080", + URL: "http://proxy.example.com:8080", }, NoProxy: []string{"example.com"}, } diff --git a/controllers/actions.github.com/ephemeralrunnerset_controller_test.go b/controllers/actions.github.com/ephemeralrunnerset_controller_test.go index c345965256..50a60bcfac 100644 --- a/controllers/actions.github.com/ephemeralrunnerset_controller_test.go +++ b/controllers/actions.github.com/ephemeralrunnerset_controller_test.go @@ -1327,11 +1327,11 @@ var _ = Describe("Test EphemeralRunnerSet controller with proxy settings", func( RunnerScaleSetID: 100, Proxy: &v1alpha1.ProxyConfig{ HTTP: &v1alpha1.ProxyServerConfig{ - Url: "http://proxy.example.com", + URL: "http://proxy.example.com", CredentialSecretRef: secretCredentials.Name, }, HTTPS: &v1alpha1.ProxyServerConfig{ - Url: "https://proxy.example.com", + URL: "https://proxy.example.com", CredentialSecretRef: secretCredentials.Name, }, NoProxy: []string{"example.com", "example.org"}, @@ -1510,7 +1510,7 @@ var _ = Describe("Test EphemeralRunnerSet controller with proxy settings", func( RunnerScaleSetID: 100, Proxy: &v1alpha1.ProxyConfig{ HTTP: &v1alpha1.ProxyServerConfig{ - Url: proxy.URL, + URL: proxy.URL, CredentialSecretRef: "proxy-credentials", }, }, diff --git a/controllers/actions.github.com/secretresolver/secret_resolver.go b/controllers/actions.github.com/secretresolver/secret_resolver.go index 5f3e95612d..b9f9d9ccba 100644 --- a/controllers/actions.github.com/secretresolver/secret_resolver.go +++ b/controllers/actions.github.com/secretresolver/secret_resolver.go @@ -85,9 +85,9 @@ func (sr *SecretResolver) GetActionsService(ctx context.Context, obj object.Acti } if proxy.HTTP != nil { - u, err := url.Parse(proxy.HTTP.Url) + u, err := url.Parse(proxy.HTTP.URL) if err != nil { - return nil, fmt.Errorf("failed to parse proxy http url %q: %w", proxy.HTTP.Url, err) + return nil, fmt.Errorf("failed to parse proxy http url %q: %w", proxy.HTTP.URL, err) } if ref := proxy.HTTP.CredentialSecretRef; ref != "" { @@ -101,9 +101,9 @@ func (sr *SecretResolver) GetActionsService(ctx context.Context, obj object.Acti } if proxy.HTTPS != nil { - u, err := url.Parse(proxy.HTTPS.Url) + u, err := url.Parse(proxy.HTTPS.URL) if err != nil { - return nil, fmt.Errorf("failed to parse proxy https url %q: %w", proxy.HTTPS.Url, err) + return nil, fmt.Errorf("failed to parse proxy https url %q: %w", proxy.HTTPS.URL, err) } if ref := proxy.HTTPS.CredentialSecretRef; ref != "" { From 0a676e3018330278bc5ecaf150b094898c3a8508 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 12 Jun 2026 16:28:17 +0200 Subject: [PATCH 04/12] Updates: runner to v2.335.1 (#4523) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- Makefile | 2 +- runner/Makefile | 2 +- runner/VERSION | 2 +- test/e2e/e2e_test.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 1a99ec2137..c52fefd2a9 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ endif DOCKER_USER ?= $(shell echo ${DOCKER_IMAGE_NAME} | cut -d / -f1) VERSION ?= dev COMMIT_SHA = $(shell git rev-parse HEAD) -RUNNER_VERSION ?= 2.334.0 +RUNNER_VERSION ?= 2.335.1 TARGETPLATFORM ?= $(shell arch) RUNNER_NAME ?= ${DOCKER_USER}/actions-runner RUNNER_TAG ?= ${VERSION} diff --git a/runner/Makefile b/runner/Makefile index 5fefc0448c..c72db6945b 100644 --- a/runner/Makefile +++ b/runner/Makefile @@ -6,7 +6,7 @@ DIND_ROOTLESS_RUNNER_NAME ?= ${DOCKER_USER}/actions-runner-dind-rootless OS_IMAGE ?= ubuntu-22.04 TARGETPLATFORM ?= $(shell arch) -RUNNER_VERSION ?= 2.334.0 +RUNNER_VERSION ?= 2.335.1 RUNNER_CONTAINER_HOOKS_VERSION ?= 0.8.1 DOCKER_VERSION ?= 28.0.4 diff --git a/runner/VERSION b/runner/VERSION index db802e9729..352e8556dd 100644 --- a/runner/VERSION +++ b/runner/VERSION @@ -1,2 +1,2 @@ -RUNNER_VERSION=2.334.0 +RUNNER_VERSION=2.335.1 RUNNER_CONTAINER_HOOKS_VERSION=0.8.1 \ No newline at end of file diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index b56a574975..f257f31ea5 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -36,7 +36,7 @@ var ( testResultCMNamePrefix = "test-result-" - RunnerVersion = "2.334.0" + RunnerVersion = "2.335.1" RunnerContainerHooksVersion = "0.8.1" ) From b4da30412ba0a3831df4f2438246662c9957ac8e Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Mon, 15 Jun 2026 12:51:13 +0200 Subject: [PATCH 05/12] Fix typo and rename status to phase (#4506) --- .../actions.github.com/v1alpha1/autoscalingrunnerset_types.go | 2 +- apis/actions.github.com/v1alpha1/ephemeralrunner_types.go | 2 +- apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go | 1 + .../crds/actions.github.com_autoscalingrunnersets.yaml | 4 ++-- .../crds/actions.github.com_ephemeralrunners.yaml | 2 +- .../crds/actions.github.com_ephemeralrunnersets.yaml | 3 +++ .../crds/actions.github.com_autoscalingrunnersets.yaml | 4 ++-- .../crds/actions.github.com_ephemeralrunners.yaml | 2 +- .../crds/actions.github.com_ephemeralrunnersets.yaml | 3 +++ .../crd/bases/actions.github.com_autoscalingrunnersets.yaml | 4 ++-- config/crd/bases/actions.github.com_ephemeralrunners.yaml | 2 +- config/crd/bases/actions.github.com_ephemeralrunnersets.yaml | 3 +++ 12 files changed, 21 insertions(+), 11 deletions(-) diff --git a/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go b/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go index 69ad40c92e..5f6dc2563a 100644 --- a/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go +++ b/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go @@ -37,7 +37,7 @@ import ( // +kubebuilder:printcolumn:JSONPath=".spec.minRunners",name=Minimum Runners,type=integer // +kubebuilder:printcolumn:JSONPath=".spec.maxRunners",name=Maximum Runners,type=integer // +kubebuilder:printcolumn:JSONPath=".status.currentRunners",name=Current Runners,type=integer -// +kubebuilder:printcolumn:JSONPath=".status.state",name=State,type=string +// +kubebuilder:printcolumn:JSONPath=".status.phase",name=Phase,type=string // +kubebuilder:printcolumn:JSONPath=".status.pendingEphemeralRunners",name=Pending Runners,type=integer // +kubebuilder:printcolumn:JSONPath=".status.runningEphemeralRunners",name=Running Runners,type=integer // +kubebuilder:printcolumn:JSONPath=".status.finishedEphemeralRunners",name=Finished Runners,type=integer diff --git a/apis/actions.github.com/v1alpha1/ephemeralrunner_types.go b/apis/actions.github.com/v1alpha1/ephemeralrunner_types.go index 6c2c03150e..744afab505 100644 --- a/apis/actions.github.com/v1alpha1/ephemeralrunner_types.go +++ b/apis/actions.github.com/v1alpha1/ephemeralrunner_types.go @@ -30,7 +30,7 @@ const EphemeralRunnerContainerName = "runner" // +kubebuilder:subresource:status // +kubebuilder:printcolumn:JSONPath=".spec.githubConfigUrl",name="GitHub Config URL",type=string // +kubebuilder:printcolumn:JSONPath=".status.runnerId",name=RunnerId,type=number -// +kubebuilder:printcolumn:JSONPath=".status.phase",name=Status,type=string +// +kubebuilder:printcolumn:JSONPath=".status.phase",name=Phase,type=string // +kubebuilder:printcolumn:JSONPath=".status.jobRepositoryName",name=JobRepository,type=string // +kubebuilder:printcolumn:JSONPath=".status.jobWorkflowRef",name=JobWorkflowRef,type=string // +kubebuilder:printcolumn:JSONPath=".status.workflowRunId",name=WorkflowRunId,type=number diff --git a/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go b/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go index 9bdd8de368..c6da517924 100644 --- a/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go +++ b/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go @@ -71,6 +71,7 @@ const ( // +kubebuilder:printcolumn:JSONPath=".status.runningEphemeralRunners",name=Running Runners,type=integer // +kubebuilder:printcolumn:JSONPath=".status.finishedEphemeralRunners",name=Finished Runners,type=integer // +kubebuilder:printcolumn:JSONPath=".status.deletingEphemeralRunners",name=Deleting Runners,type=integer +// +kubebuilder:printcolumn:JSONPath=".status.phase",name=Phase,type=string // EphemeralRunnerSet is the Schema for the ephemeralrunnersets API type EphemeralRunnerSet struct { 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 20f126255e..e56f05c6b2 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 @@ -24,8 +24,8 @@ spec: - jsonPath: .status.currentRunners name: Current Runners type: integer - - jsonPath: .status.state - name: State + - jsonPath: .status.phase + name: Phase type: string - jsonPath: .status.pendingEphemeralRunners name: Pending Runners diff --git a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunners.yaml b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunners.yaml index 6cc6707ab2..3cd90148ca 100644 --- a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunners.yaml +++ b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunners.yaml @@ -22,7 +22,7 @@ spec: name: RunnerId type: number - jsonPath: .status.phase - name: Status + name: Phase type: string - jsonPath: .status.jobRepositoryName name: JobRepository diff --git a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunnersets.yaml b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunnersets.yaml index b7a8d5e954..283cc6036e 100644 --- a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunnersets.yaml +++ b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunnersets.yaml @@ -33,6 +33,9 @@ spec: - jsonPath: .status.deletingEphemeralRunners name: Deleting Runners type: integer + - jsonPath: .status.phase + name: Phase + type: string name: v1alpha1 schema: openAPIV3Schema: 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 20f126255e..e56f05c6b2 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 @@ -24,8 +24,8 @@ spec: - jsonPath: .status.currentRunners name: Current Runners type: integer - - jsonPath: .status.state - name: State + - jsonPath: .status.phase + name: Phase type: string - jsonPath: .status.pendingEphemeralRunners name: Pending Runners diff --git a/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunners.yaml b/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunners.yaml index 6cc6707ab2..3cd90148ca 100644 --- a/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunners.yaml +++ b/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunners.yaml @@ -22,7 +22,7 @@ spec: name: RunnerId type: number - jsonPath: .status.phase - name: Status + name: Phase type: string - jsonPath: .status.jobRepositoryName name: JobRepository diff --git a/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunnersets.yaml b/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunnersets.yaml index b7a8d5e954..283cc6036e 100644 --- a/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunnersets.yaml +++ b/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunnersets.yaml @@ -33,6 +33,9 @@ spec: - jsonPath: .status.deletingEphemeralRunners name: Deleting Runners type: integer + - jsonPath: .status.phase + name: Phase + type: string name: v1alpha1 schema: openAPIV3Schema: diff --git a/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml b/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml index 20f126255e..e56f05c6b2 100644 --- a/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml +++ b/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml @@ -24,8 +24,8 @@ spec: - jsonPath: .status.currentRunners name: Current Runners type: integer - - jsonPath: .status.state - name: State + - jsonPath: .status.phase + name: Phase type: string - jsonPath: .status.pendingEphemeralRunners name: Pending Runners diff --git a/config/crd/bases/actions.github.com_ephemeralrunners.yaml b/config/crd/bases/actions.github.com_ephemeralrunners.yaml index 6cc6707ab2..3cd90148ca 100644 --- a/config/crd/bases/actions.github.com_ephemeralrunners.yaml +++ b/config/crd/bases/actions.github.com_ephemeralrunners.yaml @@ -22,7 +22,7 @@ spec: name: RunnerId type: number - jsonPath: .status.phase - name: Status + name: Phase type: string - jsonPath: .status.jobRepositoryName name: JobRepository diff --git a/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml b/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml index b7a8d5e954..283cc6036e 100644 --- a/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml +++ b/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml @@ -33,6 +33,9 @@ spec: - jsonPath: .status.deletingEphemeralRunners name: Deleting Runners type: integer + - jsonPath: .status.phase + name: Phase + type: string name: v1alpha1 schema: openAPIV3Schema: From b3a6c2e0cba17f8889df1e0ad165a9d9518603ad Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Mon, 15 Jun 2026 12:21:17 +0200 Subject: [PATCH 06/12] wip --- .../v1alpha1/autoscalinglistener_types.go | 33 ++++++++------- .../v1alpha1/autoscalingrunnerset_types.go | 19 +++++---- .../v1alpha1/ephemeralrunner_types.go | 7 +++- .../v1alpha1/ephemeralrunnerset_types.go | 11 ++++- ...tions.github.com_autoscalinglisteners.yaml | 40 +++++++++++-------- ...ions.github.com_autoscalingrunnersets.yaml | 34 +++++++++------- .../actions.github.com_ephemeralrunners.yaml | 19 ++++++--- ...ctions.github.com_ephemeralrunnersets.yaml | 23 ++++++----- ...tions.github.com_autoscalinglisteners.yaml | 40 +++++++++++-------- ...ions.github.com_autoscalingrunnersets.yaml | 34 +++++++++------- .../actions.github.com_ephemeralrunners.yaml | 19 ++++++--- ...ctions.github.com_ephemeralrunnersets.yaml | 23 ++++++----- ...tions.github.com_autoscalinglisteners.yaml | 40 +++++++++++-------- ...ions.github.com_autoscalingrunnersets.yaml | 34 +++++++++------- .../actions.github.com_ephemeralrunners.yaml | 19 ++++++--- ...ctions.github.com_ephemeralrunnersets.yaml | 23 ++++++----- 16 files changed, 252 insertions(+), 166 deletions(-) diff --git a/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go b/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go index 1ae599f1fb..3106e22546 100644 --- a/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go +++ b/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go @@ -24,36 +24,36 @@ import ( // AutoscalingListenerSpec defines the desired state of AutoscalingListener type AutoscalingListenerSpec struct { - // Required + // +required GitHubConfigURL string `json:"githubConfigUrl,omitempty"` - // Required + // +required GitHubConfigSecret string `json:"githubConfigSecret,omitempty"` - // Required + // +required RunnerScaleSetID int `json:"runnerScaleSetId,omitempty"` - // Required + // +required AutoscalingRunnerSetNamespace string `json:"autoscalingRunnerSetNamespace,omitempty"` - // Required + // +required AutoscalingRunnerSetName string `json:"autoscalingRunnerSetName,omitempty"` - // Required + // +required EphemeralRunnerSetName string `json:"ephemeralRunnerSetName,omitempty"` - // Required - // +kubebuilder:validation:Minimum:=0 + // +kubebuilder:validation:Minimum=0 + // +required MaxRunners int `json:"maxRunners,omitempty"` - // Required - // +kubebuilder:validation:Minimum:=0 + // +kubebuilder:validation:Minimum=0 + // +required MinRunners int `json:"minRunners,omitempty"` - // Required + // +required Image string `json:"image,omitempty"` - // Required + // +required ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` // +optional @@ -99,17 +99,22 @@ type AutoscalingListenerStatus struct{} // AutoscalingListener is the Schema for the autoscalinglisteners API type AutoscalingListener struct { - metav1.TypeMeta `json:",inline"` + metav1.TypeMeta `json:",inline"` + // +optional metav1.ObjectMeta `json:"metadata,omitempty"` - Spec AutoscalingListenerSpec `json:"spec,omitempty"` + // +optional + Spec AutoscalingListenerSpec `json:"spec,omitempty"` + // +optional Status AutoscalingListenerStatus `json:"status,omitempty"` } +// AutoscalingListenerList is a list of AutoscalingListener resources // +kubebuilder:object:root=true // AutoscalingListenerList contains a list of AutoscalingListener type AutoscalingListenerList struct { metav1.TypeMeta `json:",inline"` + // +optional metav1.ListMeta `json:"metadata,omitempty"` Items []AutoscalingListener `json:"items"` } diff --git a/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go b/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go index 5f6dc2563a..ce0507d5ab 100644 --- a/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go +++ b/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go @@ -40,8 +40,7 @@ import ( // +kubebuilder:printcolumn:JSONPath=".status.phase",name=Phase,type=string // +kubebuilder:printcolumn:JSONPath=".status.pendingEphemeralRunners",name=Pending Runners,type=integer // +kubebuilder:printcolumn:JSONPath=".status.runningEphemeralRunners",name=Running Runners,type=integer -// +kubebuilder:printcolumn:JSONPath=".status.finishedEphemeralRunners",name=Finished Runners,type=integer -// +kubebuilder:printcolumn:JSONPath=".status.deletingEphemeralRunners",name=Deleting Runners,type=integer +// +kubebuilder:printcolumn:JSONPath=".status.failedEphemeralRunners",name=Failed Runners,type=integer // AutoscalingRunnerSet is the Schema for the autoscalingrunnersets API type AutoscalingRunnerSet struct { @@ -54,10 +53,10 @@ type AutoscalingRunnerSet struct { // AutoscalingRunnerSetSpec defines the desired state of AutoscalingRunnerSet type AutoscalingRunnerSetSpec struct { - // Required + // +required GitHubConfigUrl string `json:"githubConfigUrl,omitempty"` - // Required + // +required GitHubConfigSecret string `json:"githubConfigSecret,omitempty"` // +optional @@ -78,7 +77,7 @@ type AutoscalingRunnerSetSpec struct { // +optional VaultConfig *VaultConfig `json:"vaultConfig,omitempty"` - // Required + // +required Template corev1.PodTemplateSpec `json:"template,omitempty"` // +optional @@ -112,16 +111,16 @@ type AutoscalingRunnerSetSpec struct { EphemeralRunnerConfigSecretMetadata *ResourceMeta `json:"ephemeralRunnerConfigSecretMetadata,omitempty"` // +optional - // +kubebuilder:validation:Minimum:=0 + // +kubebuilder:validation:Minimum=0 MaxRunners *int `json:"maxRunners,omitempty"` // +optional - // +kubebuilder:validation:Minimum:=0 + // +kubebuilder:validation:Minimum=0 MinRunners *int `json:"minRunners,omitempty"` } type TLSConfig struct { - // Required + // +required CertificateFrom *TLSCertificateSource `json:"certificateFrom,omitempty"` } @@ -158,7 +157,7 @@ func (c *TLSConfig) ToCertPool(keyFetcher func(name, key string) ([]byte, error) } type TLSCertificateSource struct { - // Required + // +required ConfigMapKeyRef *corev1.ConfigMapKeySelector `json:"configMapKeyRef,omitempty"` } @@ -259,7 +258,7 @@ func (c *ProxyConfig) ProxyFunc(secretFetcher func(string) (*corev1.Secret, erro } type ProxyServerConfig struct { - // Required + // +required URL string `json:"url,omitempty"` // +optional diff --git a/apis/actions.github.com/v1alpha1/ephemeralrunner_types.go b/apis/actions.github.com/v1alpha1/ephemeralrunner_types.go index 744afab505..5ce38f8226 100644 --- a/apis/actions.github.com/v1alpha1/ephemeralrunner_types.go +++ b/apis/actions.github.com/v1alpha1/ephemeralrunner_types.go @@ -41,10 +41,13 @@ const EphemeralRunnerContainerName = "runner" // EphemeralRunner is the Schema for the ephemeralrunners API type EphemeralRunner struct { - metav1.TypeMeta `json:",inline"` + metav1.TypeMeta `json:",inline"` + // +optional metav1.ObjectMeta `json:"metadata,omitempty"` - Spec EphemeralRunnerSpec `json:"spec,omitempty"` + // +optional + Spec EphemeralRunnerSpec `json:"spec,omitempty"` + // +optional Status EphemeralRunnerStatus `json:"status,omitempty"` } diff --git a/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go b/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go index c6da517924..de411ab87c 100644 --- a/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go +++ b/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go @@ -23,10 +23,13 @@ import ( // EphemeralRunnerSetSpec defines the desired state of EphemeralRunnerSet type EphemeralRunnerSetSpec struct { // Replicas is the number of desired EphemeralRunner resources in the k8s namespace. + // +optional Replicas int `json:"replicas,omitempty"` // PatchID is the unique identifier for the patch issued by the listener app + // +optional PatchID int `json:"patchID"` // EphemeralRunnerSpec is the spec of the ephemeral runner + // +optional EphemeralRunnerSpec EphemeralRunnerSpec `json:"ephemeralRunnerSpec,omitempty"` // EphemeralRunnerMetadata is the metadata to be applied to all ephemeral runners created by this set. // If the EphemeralRunnerMetadata is updated, the update applies to new ephemeral runners created after the update, @@ -39,6 +42,7 @@ type EphemeralRunnerSetSpec struct { type EphemeralRunnerSetStatus struct { // CurrentReplicas is the number of currently running EphemeralRunner resources being managed by this EphemeralRunnerSet. // +kubebuilder:validation:Minimum=0 + // +optional CurrentReplicas int `json:"currentReplicas"` // +optional // +kubebuilder:validation:Minimum=0 @@ -75,10 +79,13 @@ const ( // EphemeralRunnerSet is the Schema for the ephemeralrunnersets API type EphemeralRunnerSet struct { - metav1.TypeMeta `json:",inline"` + metav1.TypeMeta `json:",inline"` + // +optional metav1.ObjectMeta `json:"metadata,omitempty"` - Spec EphemeralRunnerSetSpec `json:"spec,omitempty"` + // +optional + Spec EphemeralRunnerSetSpec `json:"spec,omitempty"` + // +optional Status EphemeralRunnerSetStatus `json:"status,omitempty"` } diff --git a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_autoscalinglisteners.yaml b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_autoscalinglisteners.yaml index 20e57e3398..756cb9662d 100644 --- a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_autoscalinglisteners.yaml +++ b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_autoscalinglisteners.yaml @@ -51,10 +51,8 @@ spec: description: AutoscalingListenerSpec defines the desired state of AutoscalingListener properties: autoscalingRunnerSetName: - description: Required type: string autoscalingRunnerSetNamespace: - description: Required type: string configSecretMetadata: description: ResourceMeta carries metadata common to all internal @@ -70,21 +68,17 @@ spec: type: object type: object ephemeralRunnerSetName: - description: Required type: string githubConfigSecret: - description: Required type: string githubConfigUrl: - description: Required type: string githubServerTLS: properties: certificateFrom: - description: Required properties: configMapKeyRef: - description: Required + description: Selects a key from a ConfigMap. properties: key: description: The key to select. @@ -106,13 +100,15 @@ spec: - key type: object x-kubernetes-map-type: atomic + required: + - configMapKeyRef type: object + required: + - certificateFrom type: object image: - description: Required type: string imagePullSecrets: - description: Required items: description: |- LocalObjectReference contains enough information to let you locate the @@ -131,7 +127,6 @@ spec: x-kubernetes-map-type: atomic type: array maxRunners: - description: Required minimum: 0 type: integer metrics: @@ -183,7 +178,6 @@ spec: type: object type: object minRunners: - description: Required minimum: 0 type: integer proxy: @@ -193,16 +187,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -236,7 +232,6 @@ spec: type: object type: object runnerScaleSetId: - description: Required type: integer serviceAccountMetadata: description: ResourceMeta carries metadata common to all internal @@ -8782,16 +8777,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -8804,6 +8801,17 @@ spec: It is used to identify which vault integration should be used to resolve secrets. type: string type: object + required: + - autoscalingRunnerSetName + - autoscalingRunnerSetNamespace + - ephemeralRunnerSetName + - githubConfigSecret + - githubConfigUrl + - image + - imagePullSecrets + - maxRunners + - minRunners + - runnerScaleSetId type: object status: description: AutoscalingListenerStatus defines the observed state of AutoscalingListener 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 e56f05c6b2..122b52defe 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 @@ -33,11 +33,8 @@ spec: - jsonPath: .status.runningEphemeralRunners name: Running Runners type: integer - - jsonPath: .status.finishedEphemeralRunners - name: Finished Runners - type: integer - - jsonPath: .status.deletingEphemeralRunners - name: Deleting Runners + - jsonPath: .status.failedEphemeralRunners + name: Failed Runners type: integer name: v1alpha1 schema: @@ -113,18 +110,15 @@ spec: type: object type: object githubConfigSecret: - description: Required type: string githubConfigUrl: - description: Required type: string githubServerTLS: properties: certificateFrom: - description: Required properties: configMapKeyRef: - description: Required + description: Selects a key from a ConfigMap. properties: key: description: The key to select. @@ -145,7 +139,11 @@ spec: - key type: object x-kubernetes-map-type: atomic + required: + - configMapKeyRef type: object + required: + - certificateFrom type: object listenerConfigSecretMetadata: description: ResourceMeta carries metadata common to all internal resources @@ -8366,16 +8364,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -8391,7 +8391,7 @@ spec: runnerScaleSetName: type: string template: - description: Required + description: PodTemplateSpec describes the data a pod should have when created from a template properties: metadata: description: |- @@ -16517,16 +16517,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -16539,6 +16541,10 @@ spec: It is used to identify which vault integration should be used to resolve secrets. type: string type: object + required: + - githubConfigSecret + - githubConfigUrl + - template type: object status: description: AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet diff --git a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunners.yaml b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunners.yaml index 3cd90148ca..0b413f417d 100644 --- a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunners.yaml +++ b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunners.yaml @@ -89,10 +89,9 @@ spec: githubServerTLS: properties: certificateFrom: - description: Required properties: configMapKeyRef: - description: Required + description: Selects a key from a ConfigMap. properties: key: description: The key to select. @@ -113,7 +112,11 @@ spec: - key type: object x-kubernetes-map-type: atomic + required: + - configMapKeyRef type: object + required: + - certificateFrom type: object metadata: description: |- @@ -144,16 +147,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -8268,16 +8273,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: diff --git a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunnersets.yaml b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunnersets.yaml index 283cc6036e..6ff8ca2dfa 100644 --- a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunnersets.yaml +++ b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunnersets.yaml @@ -98,10 +98,9 @@ spec: githubServerTLS: properties: certificateFrom: - description: Required properties: configMapKeyRef: - description: Required + description: Selects a key from a ConfigMap. properties: key: description: The key to select. @@ -122,7 +121,11 @@ spec: - key type: object x-kubernetes-map-type: atomic + required: + - configMapKeyRef type: object + required: + - certificateFrom type: object metadata: description: |- @@ -153,16 +156,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -8277,16 +8282,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -8310,8 +8317,6 @@ spec: replicas: description: Replicas is the number of desired EphemeralRunner resources in the k8s namespace. type: integer - required: - - patchID type: object status: description: EphemeralRunnerSetStatus defines the observed state of EphemeralRunnerSet @@ -8332,8 +8337,6 @@ spec: runningEphemeralRunners: minimum: 0 type: integer - required: - - currentReplicas type: object type: object served: true diff --git a/charts/gha-runner-scale-set-controller/crds/actions.github.com_autoscalinglisteners.yaml b/charts/gha-runner-scale-set-controller/crds/actions.github.com_autoscalinglisteners.yaml index 20e57e3398..756cb9662d 100644 --- a/charts/gha-runner-scale-set-controller/crds/actions.github.com_autoscalinglisteners.yaml +++ b/charts/gha-runner-scale-set-controller/crds/actions.github.com_autoscalinglisteners.yaml @@ -51,10 +51,8 @@ spec: description: AutoscalingListenerSpec defines the desired state of AutoscalingListener properties: autoscalingRunnerSetName: - description: Required type: string autoscalingRunnerSetNamespace: - description: Required type: string configSecretMetadata: description: ResourceMeta carries metadata common to all internal @@ -70,21 +68,17 @@ spec: type: object type: object ephemeralRunnerSetName: - description: Required type: string githubConfigSecret: - description: Required type: string githubConfigUrl: - description: Required type: string githubServerTLS: properties: certificateFrom: - description: Required properties: configMapKeyRef: - description: Required + description: Selects a key from a ConfigMap. properties: key: description: The key to select. @@ -106,13 +100,15 @@ spec: - key type: object x-kubernetes-map-type: atomic + required: + - configMapKeyRef type: object + required: + - certificateFrom type: object image: - description: Required type: string imagePullSecrets: - description: Required items: description: |- LocalObjectReference contains enough information to let you locate the @@ -131,7 +127,6 @@ spec: x-kubernetes-map-type: atomic type: array maxRunners: - description: Required minimum: 0 type: integer metrics: @@ -183,7 +178,6 @@ spec: type: object type: object minRunners: - description: Required minimum: 0 type: integer proxy: @@ -193,16 +187,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -236,7 +232,6 @@ spec: type: object type: object runnerScaleSetId: - description: Required type: integer serviceAccountMetadata: description: ResourceMeta carries metadata common to all internal @@ -8782,16 +8777,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -8804,6 +8801,17 @@ spec: It is used to identify which vault integration should be used to resolve secrets. type: string type: object + required: + - autoscalingRunnerSetName + - autoscalingRunnerSetNamespace + - ephemeralRunnerSetName + - githubConfigSecret + - githubConfigUrl + - image + - imagePullSecrets + - maxRunners + - minRunners + - runnerScaleSetId type: object status: description: AutoscalingListenerStatus defines the observed state of AutoscalingListener 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 e56f05c6b2..122b52defe 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 @@ -33,11 +33,8 @@ spec: - jsonPath: .status.runningEphemeralRunners name: Running Runners type: integer - - jsonPath: .status.finishedEphemeralRunners - name: Finished Runners - type: integer - - jsonPath: .status.deletingEphemeralRunners - name: Deleting Runners + - jsonPath: .status.failedEphemeralRunners + name: Failed Runners type: integer name: v1alpha1 schema: @@ -113,18 +110,15 @@ spec: type: object type: object githubConfigSecret: - description: Required type: string githubConfigUrl: - description: Required type: string githubServerTLS: properties: certificateFrom: - description: Required properties: configMapKeyRef: - description: Required + description: Selects a key from a ConfigMap. properties: key: description: The key to select. @@ -145,7 +139,11 @@ spec: - key type: object x-kubernetes-map-type: atomic + required: + - configMapKeyRef type: object + required: + - certificateFrom type: object listenerConfigSecretMetadata: description: ResourceMeta carries metadata common to all internal resources @@ -8366,16 +8364,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -8391,7 +8391,7 @@ spec: runnerScaleSetName: type: string template: - description: Required + description: PodTemplateSpec describes the data a pod should have when created from a template properties: metadata: description: |- @@ -16517,16 +16517,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -16539,6 +16541,10 @@ spec: It is used to identify which vault integration should be used to resolve secrets. type: string type: object + required: + - githubConfigSecret + - githubConfigUrl + - template type: object status: description: AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet diff --git a/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunners.yaml b/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunners.yaml index 3cd90148ca..0b413f417d 100644 --- a/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunners.yaml +++ b/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunners.yaml @@ -89,10 +89,9 @@ spec: githubServerTLS: properties: certificateFrom: - description: Required properties: configMapKeyRef: - description: Required + description: Selects a key from a ConfigMap. properties: key: description: The key to select. @@ -113,7 +112,11 @@ spec: - key type: object x-kubernetes-map-type: atomic + required: + - configMapKeyRef type: object + required: + - certificateFrom type: object metadata: description: |- @@ -144,16 +147,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -8268,16 +8273,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: diff --git a/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunnersets.yaml b/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunnersets.yaml index 283cc6036e..6ff8ca2dfa 100644 --- a/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunnersets.yaml +++ b/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunnersets.yaml @@ -98,10 +98,9 @@ spec: githubServerTLS: properties: certificateFrom: - description: Required properties: configMapKeyRef: - description: Required + description: Selects a key from a ConfigMap. properties: key: description: The key to select. @@ -122,7 +121,11 @@ spec: - key type: object x-kubernetes-map-type: atomic + required: + - configMapKeyRef type: object + required: + - certificateFrom type: object metadata: description: |- @@ -153,16 +156,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -8277,16 +8282,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -8310,8 +8317,6 @@ spec: replicas: description: Replicas is the number of desired EphemeralRunner resources in the k8s namespace. type: integer - required: - - patchID type: object status: description: EphemeralRunnerSetStatus defines the observed state of EphemeralRunnerSet @@ -8332,8 +8337,6 @@ spec: runningEphemeralRunners: minimum: 0 type: integer - required: - - currentReplicas type: object type: object served: true diff --git a/config/crd/bases/actions.github.com_autoscalinglisteners.yaml b/config/crd/bases/actions.github.com_autoscalinglisteners.yaml index 20e57e3398..756cb9662d 100644 --- a/config/crd/bases/actions.github.com_autoscalinglisteners.yaml +++ b/config/crd/bases/actions.github.com_autoscalinglisteners.yaml @@ -51,10 +51,8 @@ spec: description: AutoscalingListenerSpec defines the desired state of AutoscalingListener properties: autoscalingRunnerSetName: - description: Required type: string autoscalingRunnerSetNamespace: - description: Required type: string configSecretMetadata: description: ResourceMeta carries metadata common to all internal @@ -70,21 +68,17 @@ spec: type: object type: object ephemeralRunnerSetName: - description: Required type: string githubConfigSecret: - description: Required type: string githubConfigUrl: - description: Required type: string githubServerTLS: properties: certificateFrom: - description: Required properties: configMapKeyRef: - description: Required + description: Selects a key from a ConfigMap. properties: key: description: The key to select. @@ -106,13 +100,15 @@ spec: - key type: object x-kubernetes-map-type: atomic + required: + - configMapKeyRef type: object + required: + - certificateFrom type: object image: - description: Required type: string imagePullSecrets: - description: Required items: description: |- LocalObjectReference contains enough information to let you locate the @@ -131,7 +127,6 @@ spec: x-kubernetes-map-type: atomic type: array maxRunners: - description: Required minimum: 0 type: integer metrics: @@ -183,7 +178,6 @@ spec: type: object type: object minRunners: - description: Required minimum: 0 type: integer proxy: @@ -193,16 +187,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -236,7 +232,6 @@ spec: type: object type: object runnerScaleSetId: - description: Required type: integer serviceAccountMetadata: description: ResourceMeta carries metadata common to all internal @@ -8782,16 +8777,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -8804,6 +8801,17 @@ spec: It is used to identify which vault integration should be used to resolve secrets. type: string type: object + required: + - autoscalingRunnerSetName + - autoscalingRunnerSetNamespace + - ephemeralRunnerSetName + - githubConfigSecret + - githubConfigUrl + - image + - imagePullSecrets + - maxRunners + - minRunners + - runnerScaleSetId type: object status: description: AutoscalingListenerStatus defines the observed state of AutoscalingListener diff --git a/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml b/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml index e56f05c6b2..122b52defe 100644 --- a/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml +++ b/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml @@ -33,11 +33,8 @@ spec: - jsonPath: .status.runningEphemeralRunners name: Running Runners type: integer - - jsonPath: .status.finishedEphemeralRunners - name: Finished Runners - type: integer - - jsonPath: .status.deletingEphemeralRunners - name: Deleting Runners + - jsonPath: .status.failedEphemeralRunners + name: Failed Runners type: integer name: v1alpha1 schema: @@ -113,18 +110,15 @@ spec: type: object type: object githubConfigSecret: - description: Required type: string githubConfigUrl: - description: Required type: string githubServerTLS: properties: certificateFrom: - description: Required properties: configMapKeyRef: - description: Required + description: Selects a key from a ConfigMap. properties: key: description: The key to select. @@ -145,7 +139,11 @@ spec: - key type: object x-kubernetes-map-type: atomic + required: + - configMapKeyRef type: object + required: + - certificateFrom type: object listenerConfigSecretMetadata: description: ResourceMeta carries metadata common to all internal resources @@ -8366,16 +8364,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -8391,7 +8391,7 @@ spec: runnerScaleSetName: type: string template: - description: Required + description: PodTemplateSpec describes the data a pod should have when created from a template properties: metadata: description: |- @@ -16517,16 +16517,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -16539,6 +16541,10 @@ spec: It is used to identify which vault integration should be used to resolve secrets. type: string type: object + required: + - githubConfigSecret + - githubConfigUrl + - template type: object status: description: AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet diff --git a/config/crd/bases/actions.github.com_ephemeralrunners.yaml b/config/crd/bases/actions.github.com_ephemeralrunners.yaml index 3cd90148ca..0b413f417d 100644 --- a/config/crd/bases/actions.github.com_ephemeralrunners.yaml +++ b/config/crd/bases/actions.github.com_ephemeralrunners.yaml @@ -89,10 +89,9 @@ spec: githubServerTLS: properties: certificateFrom: - description: Required properties: configMapKeyRef: - description: Required + description: Selects a key from a ConfigMap. properties: key: description: The key to select. @@ -113,7 +112,11 @@ spec: - key type: object x-kubernetes-map-type: atomic + required: + - configMapKeyRef type: object + required: + - certificateFrom type: object metadata: description: |- @@ -144,16 +147,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -8268,16 +8273,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: diff --git a/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml b/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml index 283cc6036e..6ff8ca2dfa 100644 --- a/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml +++ b/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml @@ -98,10 +98,9 @@ spec: githubServerTLS: properties: certificateFrom: - description: Required properties: configMapKeyRef: - description: Required + description: Selects a key from a ConfigMap. properties: key: description: The key to select. @@ -122,7 +121,11 @@ spec: - key type: object x-kubernetes-map-type: atomic + required: + - configMapKeyRef type: object + required: + - certificateFrom type: object metadata: description: |- @@ -153,16 +156,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -8277,16 +8282,18 @@ spec: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object https: properties: credentialSecretRef: type: string url: - description: Required type: string + required: + - url type: object noProxy: items: @@ -8310,8 +8317,6 @@ spec: replicas: description: Replicas is the number of desired EphemeralRunner resources in the k8s namespace. type: integer - required: - - patchID type: object status: description: EphemeralRunnerSetStatus defines the observed state of EphemeralRunnerSet @@ -8332,8 +8337,6 @@ spec: runningEphemeralRunners: minimum: 0 type: integer - required: - - currentReplicas type: object type: object served: true From 9452b9eff053fb8117b87a98da74bdf49840547d Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Mon, 15 Jun 2026 12:41:07 +0200 Subject: [PATCH 07/12] wip --- .../v1alpha1/autoscalinglistener_types.go | 3 ++- .../v1alpha1/autoscalingrunnerset_types.go | 7 +++++-- .../crds/actions.github.com_autoscalinglisteners.yaml | 2 +- .../crds/actions.github.com_autoscalinglisteners.yaml | 2 +- .../crd/bases/actions.github.com_autoscalinglisteners.yaml | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go b/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go index 3106e22546..b35705f8a3 100644 --- a/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go +++ b/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go @@ -31,6 +31,7 @@ type AutoscalingListenerSpec struct { GitHubConfigSecret string `json:"githubConfigSecret,omitempty"` // +required + // +kubebuilder:validation:Minimum=1 RunnerScaleSetID int `json:"runnerScaleSetId,omitempty"` // +required @@ -53,7 +54,7 @@ type AutoscalingListenerSpec struct { // +required Image string `json:"image,omitempty"` - // +required + // +optional ImagePullSecrets []corev1.LocalObjectReference `json:"imagePullSecrets,omitempty"` // +optional diff --git a/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go b/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go index ce0507d5ab..23a3230cf6 100644 --- a/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go +++ b/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go @@ -44,10 +44,13 @@ import ( // AutoscalingRunnerSet is the Schema for the autoscalingrunnersets API type AutoscalingRunnerSet struct { - metav1.TypeMeta `json:",inline"` + metav1.TypeMeta `json:",inline"` + // +optional metav1.ObjectMeta `json:"metadata,omitempty"` - Spec AutoscalingRunnerSetSpec `json:"spec,omitempty"` + // +optional + Spec AutoscalingRunnerSetSpec `json:"spec,omitempty"` + // +optional Status AutoscalingRunnerSetStatus `json:"status,omitempty"` } diff --git a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_autoscalinglisteners.yaml b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_autoscalinglisteners.yaml index 756cb9662d..9265298660 100644 --- a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_autoscalinglisteners.yaml +++ b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_autoscalinglisteners.yaml @@ -232,6 +232,7 @@ spec: type: object type: object runnerScaleSetId: + minimum: 1 type: integer serviceAccountMetadata: description: ResourceMeta carries metadata common to all internal @@ -8808,7 +8809,6 @@ spec: - githubConfigSecret - githubConfigUrl - image - - imagePullSecrets - maxRunners - minRunners - runnerScaleSetId diff --git a/charts/gha-runner-scale-set-controller/crds/actions.github.com_autoscalinglisteners.yaml b/charts/gha-runner-scale-set-controller/crds/actions.github.com_autoscalinglisteners.yaml index 756cb9662d..9265298660 100644 --- a/charts/gha-runner-scale-set-controller/crds/actions.github.com_autoscalinglisteners.yaml +++ b/charts/gha-runner-scale-set-controller/crds/actions.github.com_autoscalinglisteners.yaml @@ -232,6 +232,7 @@ spec: type: object type: object runnerScaleSetId: + minimum: 1 type: integer serviceAccountMetadata: description: ResourceMeta carries metadata common to all internal @@ -8808,7 +8809,6 @@ spec: - githubConfigSecret - githubConfigUrl - image - - imagePullSecrets - maxRunners - minRunners - runnerScaleSetId diff --git a/config/crd/bases/actions.github.com_autoscalinglisteners.yaml b/config/crd/bases/actions.github.com_autoscalinglisteners.yaml index 756cb9662d..9265298660 100644 --- a/config/crd/bases/actions.github.com_autoscalinglisteners.yaml +++ b/config/crd/bases/actions.github.com_autoscalinglisteners.yaml @@ -232,6 +232,7 @@ spec: type: object type: object runnerScaleSetId: + minimum: 1 type: integer serviceAccountMetadata: description: ResourceMeta carries metadata common to all internal @@ -8808,7 +8809,6 @@ spec: - githubConfigSecret - githubConfigUrl - image - - imagePullSecrets - maxRunners - minRunners - runnerScaleSetId From 9dd6c62452adb0cac40bcf544679774edb94f8dd Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Mon, 15 Jun 2026 16:22:15 +0200 Subject: [PATCH 08/12] Fix ephemeral runner set annotation propagation --- .../templates/manager_role.yaml | 2 + .../templates/manager_role.yaml | 2 + .../autoscalinglistener_controller.go | 14 +-- .../autoscalingrunnerset_controller.go | 22 +--- .../autoscalingrunnerset_controller_test.go | 10 +- .../ephemeralrunnerset_controller.go | 6 +- .../actions.github.com/resourcebuilder.go | 105 ++++++++++-------- .../resourcebuilder_test.go | 8 +- 8 files changed, 87 insertions(+), 82 deletions(-) diff --git a/charts/gha-runner-scale-set-experimental/templates/manager_role.yaml b/charts/gha-runner-scale-set-experimental/templates/manager_role.yaml index 2990ccc49f..a5c9a258ca 100644 --- a/charts/gha-runner-scale-set-experimental/templates/manager_role.yaml +++ b/charts/gha-runner-scale-set-experimental/templates/manager_role.yaml @@ -17,6 +17,8 @@ rules: verbs: - create - delete + - update + - patch - get - apiGroups: - "" diff --git a/charts/gha-runner-scale-set/templates/manager_role.yaml b/charts/gha-runner-scale-set/templates/manager_role.yaml index bbf9279999..88ca5a3f76 100644 --- a/charts/gha-runner-scale-set/templates/manager_role.yaml +++ b/charts/gha-runner-scale-set/templates/manager_role.yaml @@ -41,6 +41,8 @@ rules: verbs: - create - delete + - update + - patch - get - apiGroups: - "" diff --git a/controllers/actions.github.com/autoscalinglistener_controller.go b/controllers/actions.github.com/autoscalinglistener_controller.go index 7d12f895d6..dc8fc5bf97 100644 --- a/controllers/actions.github.com/autoscalinglistener_controller.go +++ b/controllers/actions.github.com/autoscalinglistener_controller.go @@ -170,7 +170,7 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl. updatedServiceAccount.Labels = desiredLabels shouldUpdate = true } - desiredAnnotations := r.mergeAnnotations(serviceAccount.Annotations, desiredServiceAccount.Annotations) + desiredAnnotations := r.filterAndMergeAnnotations(serviceAccount.Annotations, desiredServiceAccount.Annotations) if !maps.Equal(serviceAccount.Annotations, desiredAnnotations) { updatedServiceAccount.Annotations = desiredAnnotations shouldUpdate = true @@ -214,7 +214,7 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl. updatedRole.Labels = desiredLabels shouldUpdate = true } - desiredAnnotations := r.mergeAnnotations(listenerRole.Annotations, desiredRole.Annotations) + desiredAnnotations := r.filterAndMergeAnnotations(listenerRole.Annotations, desiredRole.Annotations) if !maps.Equal(listenerRole.Annotations, desiredAnnotations) { updatedRole.Annotations = desiredAnnotations shouldUpdate = true @@ -257,7 +257,7 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl. updatedRoleBinding.Labels = desiredLabels shouldUpdate = true } - desiredAnnotations := r.mergeAnnotations(listenerRoleBinding.Annotations, desiredRoleBinding.Annotations) + desiredAnnotations := r.filterAndMergeAnnotations(listenerRoleBinding.Annotations, desiredRoleBinding.Annotations) if !maps.Equal(listenerRoleBinding.Annotations, desiredAnnotations) { updatedRoleBinding.Annotations = desiredAnnotations shouldUpdate = true @@ -313,7 +313,7 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl. updatedProxySecret.Labels = desiredLabels shouldUpdate = true } - desiredAnnotations := r.mergeAnnotations(proxySecret.Annotations, desiredListenerProxy.Annotations) + desiredAnnotations := r.filterAndMergeAnnotations(proxySecret.Annotations, desiredListenerProxy.Annotations) if !maps.Equal(proxySecret.Annotations, desiredAnnotations) { updatedProxySecret.Annotations = desiredAnnotations shouldUpdate = true @@ -400,7 +400,7 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl. updatedSecret.Labels = desiredLabels shouldUpdate = true } - desiredAnnotations := r.mergeAnnotations(listenerConfigSecret.Annotations, desiredSecret.Annotations) + desiredAnnotations := r.filterAndMergeAnnotations(listenerConfigSecret.Annotations, desiredSecret.Annotations) if !maps.Equal(listenerConfigSecret.Annotations, desiredAnnotations) { updatedSecret.Annotations = desiredAnnotations shouldUpdate = true @@ -467,7 +467,7 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl. return ctrl.Result{}, err } - shouldReCreate := desiredPod.Annotations[annotationKeyIntegrityHash] != listenerPod.Annotations[annotationKeyIntegrityHash] + shouldReCreate := desiredPod.Annotations[AnnotationKeyIntegrityHash] != listenerPod.Annotations[AnnotationKeyIntegrityHash] if shouldReCreate { log.Info("Listener pod dependency changed, recreating listener pod") if err := r.deleteListenerPod(ctx, &autoscalingListener, &listenerPod, log); err != nil { @@ -485,7 +485,7 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl. updatedPod.Labels = desiredLabels shouldUpdate = true } - desiredAnnotations := r.mergeAnnotations(listenerPod.Annotations, desiredPod.Annotations) + desiredAnnotations := r.filterAndMergeAnnotations(listenerPod.Annotations, desiredPod.Annotations) if !maps.Equal(listenerPod.Annotations, desiredAnnotations) { updatedPod.Annotations = desiredAnnotations shouldUpdate = true diff --git a/controllers/actions.github.com/autoscalingrunnerset_controller.go b/controllers/actions.github.com/autoscalingrunnerset_controller.go index a07cb35083..a9cc7506c5 100644 --- a/controllers/actions.github.com/autoscalingrunnerset_controller.go +++ b/controllers/actions.github.com/autoscalingrunnerset_controller.go @@ -142,13 +142,13 @@ func (r *AutoscalingRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl } // Something has changed, we need to re-apply the pending phase and change hash annotation to trigger the update of runner scale set and listener. - if targetHash := autoscalingRunnerSet.Hash(); autoscalingRunnerSet.Annotations[annotationKeyIntegrityHash] != targetHash { + if targetHash := autoscalingRunnerSet.Hash(); autoscalingRunnerSet.Annotations[AnnotationKeyIntegrityHash] != targetHash { // TODO: apply the version label original := autoscalingRunnerSet.DeepCopy() if autoscalingRunnerSet.Annotations == nil { autoscalingRunnerSet.Annotations = map[string]string{} } - autoscalingRunnerSet.Annotations[annotationKeyIntegrityHash] = targetHash + autoscalingRunnerSet.Annotations[AnnotationKeyIntegrityHash] = targetHash if err := r.Patch(ctx, &autoscalingRunnerSet, client.MergeFrom(original)); err != nil { log.Error(err, "Failed to update autoscaling runner set with new change hash and pending phase") return ctrl.Result{}, err @@ -291,24 +291,12 @@ func (r *AutoscalingRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl return ctrl.Result{}, nil } - if ephemeralRunnerSet.Annotations[annotationKeyIntegrityHash] != desired.Annotations[annotationKeyIntegrityHash] { - // When runners are actively processing jobs, defer the spec update: - // delete the listener to stop accepting new jobs, but leave the ERS - // (and its running pods) untouched until all jobs have drained. - if ephemeralRunnerSet.Status.RunningEphemeralRunners+ephemeralRunnerSet.Status.PendingEphemeralRunners > 0 { - log.Info("Ephemeral runner set spec changed but runners are still active; deleting listener to stop new jobs") - if _, err := r.cleanupListener(ctx, &autoscalingRunnerSet, log); err != nil { - log.Error(err, "Failed to clean up listener while waiting for runners to drain") - return ctrl.Result{}, err - } - return ctrl.Result{RequeueAfter: 1 * time.Second}, nil - } - + if ephemeralRunnerSet.Annotations[AnnotationKeyIntegrityHash] != desired.Annotations[AnnotationKeyIntegrityHash] { original := ephemeralRunnerSet.DeepCopy() ephemeralRunnerSet.Spec.EphemeralRunnerMetadata = desired.Spec.EphemeralRunnerMetadata ephemeralRunnerSet.Spec.EphemeralRunnerSpec = desired.Spec.EphemeralRunnerSpec ephemeralRunnerSet.Labels = r.filterAndMergeLabels(ephemeralRunnerSet.Labels, desired.Labels) - ephemeralRunnerSet.Annotations = r.mergeAnnotations(ephemeralRunnerSet.Annotations, desired.Annotations) + ephemeralRunnerSet.Annotations = r.filterAndMergeAnnotations(ephemeralRunnerSet.Annotations, desired.Annotations) log.Info("Updating ephemeral runner set spec to match the desired spec") if err := r.Patch(ctx, &ephemeralRunnerSet, client.MergeFrom(original)); err != nil { @@ -327,7 +315,7 @@ func (r *AutoscalingRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl if ephemeralRunnerLabelsModified || ephemeralRunnerAnnotationsModified || ephemeralRunnerMetadataModified { original := ephemeralRunnerSet.DeepCopy() ephemeralRunnerSet.Labels = r.filterAndMergeLabels(ephemeralRunnerSet.Labels, desired.Labels) - ephemeralRunnerSet.Annotations = r.mergeAnnotations(ephemeralRunnerSet.Annotations, desired.Annotations) + ephemeralRunnerSet.Annotations = r.filterAndMergeAnnotations(ephemeralRunnerSet.Annotations, desired.Annotations) ephemeralRunnerSet.Spec.EphemeralRunnerMetadata = desired.Spec.EphemeralRunnerMetadata log.Info("Updating ephemeral runner set metadata to match desired labels and annotations") if err := r.Patch(ctx, &ephemeralRunnerSet, client.MergeFrom(original)); err != nil { diff --git a/controllers/actions.github.com/autoscalingrunnerset_controller_test.go b/controllers/actions.github.com/autoscalingrunnerset_controller_test.go index a44d1cb9b2..d529a1ab17 100644 --- a/controllers/actions.github.com/autoscalingrunnerset_controller_test.go +++ b/controllers/actions.github.com/autoscalingrunnerset_controller_test.go @@ -478,7 +478,7 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { autoscalingRunnerSetTestInterval, ).Should(Succeed(), "EphemeralRunnerSet should be created") originalRunnerSetUID := runnerSet.UID - originalRunnerSetHash := runnerSet.Annotations[annotationKeyIntegrityHash] + originalRunnerSetHash := runnerSet.Annotations[AnnotationKeyIntegrityHash] patched := autoscalingRunnerSet.DeepCopy() patched.Spec.Template.Spec.Containers[0].Image = "ghcr.io/actions/runner:updated" @@ -492,7 +492,7 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { g.Expect(err).NotTo(HaveOccurred(), "failed to get EphemeralRunnerSet") g.Expect(current.UID).To(Equal(originalRunnerSetUID), "EphemeralRunnerSet should be updated in place") g.Expect(current.Spec.EphemeralRunnerSpec.PodTemplateSpec.Spec.Containers[0].Image).To(Equal("ghcr.io/actions/runner:updated")) - g.Expect(current.Annotations[annotationKeyIntegrityHash]).NotTo(Equal(originalRunnerSetHash), "EphemeralRunnerSet spec hash should change") + g.Expect(current.Annotations[AnnotationKeyIntegrityHash]).NotTo(Equal(originalRunnerSetHash), "EphemeralRunnerSet spec hash should change") }, autoscalingRunnerSetTestTimeout, autoscalingRunnerSetTestInterval, @@ -531,7 +531,7 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { autoscalingRunnerSetTestInterval, ).Should(Succeed(), "EphemeralRunnerSet should be created") originalRunnerSetUID := runnerSet.UID - originalRunnerSetHash := runnerSet.Annotations[annotationKeyIntegrityHash] + originalRunnerSetHash := runnerSet.Annotations[AnnotationKeyIntegrityHash] patched := autoscalingRunnerSet.DeepCopy() max := 20 @@ -557,7 +557,7 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingRunnerSet.Name, Namespace: autoscalingRunnerSet.Namespace}, current) g.Expect(err).NotTo(HaveOccurred(), "failed to get EphemeralRunnerSet") g.Expect(current.UID).To(Equal(originalRunnerSetUID), "EphemeralRunnerSet should not be recreated") - g.Expect(current.Annotations[annotationKeyIntegrityHash]).To(Equal(originalRunnerSetHash), "EphemeralRunnerSet spec should not change") + g.Expect(current.Annotations[AnnotationKeyIntegrityHash]).To(Equal(originalRunnerSetHash), "EphemeralRunnerSet spec should not change") }, time.Second*5, autoscalingRunnerSetTestInterval, @@ -906,7 +906,7 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { if patched.Annotations == nil { patched.Annotations = make(map[string]string) } - patched.Annotations[annotationKeyIntegrityHash] = "testgroup2" + patched.Annotations[AnnotationKeyIntegrityHash] = "testgroup2" patched.Spec.Template.Spec = corev1.PodSpec{ Containers: []corev1.Container{ { diff --git a/controllers/actions.github.com/ephemeralrunnerset_controller.go b/controllers/actions.github.com/ephemeralrunnerset_controller.go index f8b5f490f7..ed8e3049c3 100644 --- a/controllers/actions.github.com/ephemeralrunnerset_controller.go +++ b/controllers/actions.github.com/ephemeralrunnerset_controller.go @@ -138,7 +138,7 @@ func (r *EphemeralRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl.R // If hash spec has changed, delete idle ephemeral runners // in order to apply the change to the runners that did not yet receive a job. ephemeralRunnerIntegrityHash := ephemeralRunnerSetIntegrityHash(&ephemeralRunnerSet) - if ephemeralRunnerSet.Annotations[annotationKeyIntegrityHash] != ephemeralRunnerIntegrityHash { + if ephemeralRunnerSet.Annotations[AnnotationKeyIntegrityHash] != ephemeralRunnerIntegrityHash { log.Info("EphemeralRunnerSpec has changed, deleting idle ephemeral runners to apply the new spec") if _, err := r.cleanUpEphemeralRunners(ctx, &ephemeralRunnerSet, log); err != nil { log.Error(err, "Failed to clean up EphemeralRunners") @@ -155,7 +155,7 @@ func (r *EphemeralRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl.R if ephemeralRunnerSet.Annotations == nil { ephemeralRunnerSet.Annotations = make(map[string]string) } - ephemeralRunnerSet.Annotations[annotationKeyIntegrityHash] = ephemeralRunnerIntegrityHash + ephemeralRunnerSet.Annotations[AnnotationKeyIntegrityHash] = ephemeralRunnerIntegrityHash if err := r.Patch(ctx, &ephemeralRunnerSet, client.MergeFrom(original)); err != nil { log.Error(err, "Failed to update ephemeral runner set with new spec hash") return ctrl.Result{}, err @@ -506,7 +506,7 @@ func (r *EphemeralRunnerSetReconciler) reconcileEphemeralRunnerSetProxySecret(ct updatedProxySecret.Labels = desiredLabels shouldUpdate = true } - desiredAnnotations := r.mergeAnnotations(proxySecret.Annotations, desiredRunnerSetProxy.Annotations) + desiredAnnotations := r.filterAndMergeAnnotations(proxySecret.Annotations, desiredRunnerSetProxy.Annotations) if !maps.Equal(proxySecret.Annotations, desiredAnnotations) { updatedProxySecret.Annotations = desiredAnnotations shouldUpdate = true diff --git a/controllers/actions.github.com/resourcebuilder.go b/controllers/actions.github.com/resourcebuilder.go index fd456b56f1..7c50a092e8 100644 --- a/controllers/actions.github.com/resourcebuilder.go +++ b/controllers/actions.github.com/resourcebuilder.go @@ -46,14 +46,14 @@ var commonLabelKeys = [...]string{ LabelKeyGitHubRepository, } -// annotationKeyIntegrityHash is used as a hash of the important fields +// AnnotationKeyIntegrityHash is used as a hash of the important fields // of each resource to determine if more drastic action should be taken. // // For example, annotations/labels are not something that should modify // the behavior of a resource, while the change in spec is. Therefore, // the spec hash should contain the spec fields in order to determine // modifications. -const annotationKeyIntegrityHash = "actions.github.com/integrity-hash" +const AnnotationKeyIntegrityHash = "actions.github.com/integrity-hash" const labelValueKubernetesPartOf = "gha-runner-scale-set" @@ -165,12 +165,12 @@ func (b *ResourceBuilder) newAutoscalingListener(autoscalingRunnerSet *v1alpha1. } annotations := map[string]string{ - annotationKeyIntegrityHash: spec.Hash(), + AnnotationKeyIntegrityHash: spec.Hash(), } if autoscalingRunnerSet.Spec.AutoscalingListenerMetadata != nil { labels = b.filterAndMergeLabels(autoscalingRunnerSet.Spec.AutoscalingListenerMetadata.Labels, labels) - annotations = b.mergeAnnotations(autoscalingRunnerSet.Spec.AutoscalingListenerMetadata.Annotations, annotations) + annotations = b.filterAndMergeAnnotations(autoscalingRunnerSet.Spec.AutoscalingListenerMetadata.Annotations, annotations) } autoscalingListener := &v1alpha1.AutoscalingListener{ @@ -278,7 +278,7 @@ func (b *ResourceBuilder) newScaleSetListenerConfig(autoscalingListener *v1alpha }, } - desiredSecret.Annotations[annotationKeyIntegrityHash] = scaleSetListenerConfigIntegrityHash(desiredSecret) + desiredSecret.Annotations[AnnotationKeyIntegrityHash] = scaleSetListenerConfigIntegrityHash(desiredSecret) if err := b.setControllerReference(autoscalingListener, desiredSecret); err != nil { return nil, fmt.Errorf("failed to set controller reference for listener config secret: %w", err) @@ -426,7 +426,7 @@ func (b *ResourceBuilder) newScaleSetListenerPod( Spec: podSpec, } - newRunnerScaleSetListenerPod.Annotations[annotationKeyIntegrityHash] = scaleSetListenerPodIntegrity( + newRunnerScaleSetListenerPod.Annotations[AnnotationKeyIntegrityHash] = scaleSetListenerPodIntegrity( newRunnerScaleSetListenerPod, autoscalingListener, podConfig, @@ -468,11 +468,11 @@ func scaleSetListenerPodIntegrity( d := data{ ListenerPodSpec: &pod.Spec, - AutoscalingListenerIntegrityHash: autoscalingListener.Annotations[annotationKeyIntegrityHash], - ConfigSecretIntegrityHash: podConfig.Annotations[annotationKeyIntegrityHash], - ServiceAccountIntegrityHash: serviceAccount.Annotations[annotationKeyIntegrityHash], - RoleIntegrityHash: role.Annotations[annotationKeyIntegrityHash], - RoleBindingIntegrityHash: roleBinding.Annotations[annotationKeyIntegrityHash], + AutoscalingListenerIntegrityHash: autoscalingListener.Annotations[AnnotationKeyIntegrityHash], + ConfigSecretIntegrityHash: podConfig.Annotations[AnnotationKeyIntegrityHash], + ServiceAccountIntegrityHash: serviceAccount.Annotations[AnnotationKeyIntegrityHash], + RoleIntegrityHash: role.Annotations[AnnotationKeyIntegrityHash], + RoleBindingIntegrityHash: roleBinding.Annotations[AnnotationKeyIntegrityHash], MetricsConfig: metricsConfig, } @@ -611,10 +611,10 @@ func (b *ResourceBuilder) newScaleSetListenerServiceAccount(autoscalingListener if autoscalingListener.Spec.ServiceAccountMetadata != nil { base.Labels = b.filterAndMergeLabels(autoscalingListener.Spec.ServiceAccountMetadata.Labels, base.Labels) - base.Annotations = b.mergeAnnotations(autoscalingListener.Spec.ServiceAccountMetadata.Annotations, base.Annotations) + base.Annotations = b.filterAndMergeAnnotations(autoscalingListener.Spec.ServiceAccountMetadata.Annotations, base.Annotations) } - base.Annotations[annotationKeyIntegrityHash] = scaleSetListenerServiceAccountIntegrityHash(base) + base.Annotations[AnnotationKeyIntegrityHash] = scaleSetListenerServiceAccountIntegrityHash(base) if err := b.setControllerReference(autoscalingListener, base); err != nil { return nil, fmt.Errorf("failed to set controller reference for listener service account: %w", err) @@ -650,7 +650,7 @@ func (b *ResourceBuilder) newScaleSetListenerRole(autoscalingListener *v1alpha1. annotations := make(map[string]string) if autoscalingListener.Spec.RoleMetadata != nil { labels = b.filterAndMergeLabels(autoscalingListener.Spec.RoleMetadata.Labels, labels) - annotations = b.mergeAnnotations(autoscalingListener.Spec.RoleMetadata.Annotations, nil) + annotations = b.filterAndMergeAnnotations(autoscalingListener.Spec.RoleMetadata.Annotations, nil) } newRole := &rbacv1.Role{ @@ -663,7 +663,7 @@ func (b *ResourceBuilder) newScaleSetListenerRole(autoscalingListener *v1alpha1. Rules: rulesForListenerRole([]string{autoscalingListener.Spec.EphemeralRunnerSetName}), } - newRole.Annotations[annotationKeyIntegrityHash] = scaleSetRoleIntegrityHash(newRole) + newRole.Annotations[AnnotationKeyIntegrityHash] = scaleSetRoleIntegrityHash(newRole) return newRole } @@ -718,7 +718,7 @@ func (b *ResourceBuilder) newScaleSetListenerRoleBinding(autoscalingListener *v1 Subjects: subjects, } - newRoleBinding.Annotations[annotationKeyIntegrityHash] = scaleSetListenerRoleBindingIntegrityHash(newRoleBinding) + newRoleBinding.Annotations[AnnotationKeyIntegrityHash] = scaleSetListenerRoleBindingIntegrityHash(newRoleBinding) return newRoleBinding } @@ -777,7 +777,7 @@ func (b *ResourceBuilder) newEphemeralRunnerSet(autoscalingRunnerSet *v1alpha1.A if autoscalingRunnerSet.Spec.EphemeralRunnerSetMetadata != nil { labels = b.filterAndMergeLabels(autoscalingRunnerSet.Spec.EphemeralRunnerSetMetadata.Labels, labels) - annotations = b.mergeAnnotations(autoscalingRunnerSet.Spec.EphemeralRunnerSetMetadata.Annotations, annotations) + annotations = b.filterAndMergeAnnotations(autoscalingRunnerSet.Spec.EphemeralRunnerSetMetadata.Annotations, annotations) } newEphemeralRunnerSet := &v1alpha1.EphemeralRunnerSet{ @@ -791,7 +791,7 @@ func (b *ResourceBuilder) newEphemeralRunnerSet(autoscalingRunnerSet *v1alpha1.A Spec: spec, } - newEphemeralRunnerSet.Annotations[annotationKeyIntegrityHash] = ephemeralRunnerSetIntegrityHash(newEphemeralRunnerSet) + newEphemeralRunnerSet.Annotations[AnnotationKeyIntegrityHash] = ephemeralRunnerSetIntegrityHash(newEphemeralRunnerSet) if err := b.setControllerReference(autoscalingRunnerSet, newEphemeralRunnerSet); err != nil { return nil, fmt.Errorf("failed to set controller reference for ephemeral runner set: %w", err) @@ -825,7 +825,7 @@ func (b *ResourceBuilder) newAutoscalingListenerProxySecret(autoscalingListener Data: data, } - newProxySecret.Annotations[annotationKeyIntegrityHash] = autoscalingListenerProxySecretIntegrityHash(newProxySecret) + newProxySecret.Annotations[AnnotationKeyIntegrityHash] = autoscalingListenerProxySecretIntegrityHash(newProxySecret) if err := b.setControllerReference(autoscalingListener, newProxySecret); err != nil { return nil, fmt.Errorf("failed to set controller reference for listener proxy secret: %w", err) @@ -857,7 +857,7 @@ func (b *ResourceBuilder) newEphemeralRunner(ephemeralRunnerSet *v1alpha1.Epheme if ephemeralRunnerSet.Spec.EphemeralRunnerMetadata != nil { labels = b.filterAndMergeLabels(ephemeralRunnerSet.Spec.EphemeralRunnerMetadata.Labels, labels) - annotations = b.mergeAnnotations(ephemeralRunnerSet.Spec.EphemeralRunnerMetadata.Annotations, annotations) + annotations = b.filterAndMergeAnnotations(ephemeralRunnerSet.Spec.EphemeralRunnerMetadata.Annotations, annotations) } ephemeralRunner := &v1alpha1.EphemeralRunner{ @@ -992,7 +992,7 @@ func (b *ResourceBuilder) newEphemeralRunnerSetProxySecret(ephemeralRunnerSet *v Data: data, } - runnerPodProxySecret.Annotations[annotationKeyIntegrityHash] = ephemeralRunnerSetProxySecretZIdentityHash(runnerPodProxySecret) + runnerPodProxySecret.Annotations[AnnotationKeyIntegrityHash] = ephemeralRunnerSetProxySecretZIdentityHash(runnerPodProxySecret) if err := b.setControllerReference(ephemeralRunnerSet, runnerPodProxySecret); err != nil { return nil, fmt.Errorf("failed to set controller reference for ephemeral runner set proxy secret: %w", err) @@ -1093,40 +1093,53 @@ func trimLabelValue(val string) string { return strings.Trim(val, "-_.") } +func (b *ResourceBuilder) filterLabels(k, v string) bool { + for _, prefix := range b.ExcludeLabelPropagationPrefixes { + if strings.HasPrefix(k, prefix) { + return true + } + } + return false +} + func (b *ResourceBuilder) filterAndMergeLabels(base, overwrite map[string]string) map[string]string { + return filterAndMergeMaps(base, overwrite, b.filterLabels) +} + +func filterAndMergeMaps(base, overwrite map[string]string, filter func(k, v string) bool) map[string]string { if base == nil && overwrite == nil { return nil } - - mergedLabels := make(map[string]string, len(base)) -base: - for k, v := range base { - for _, prefix := range b.ExcludeLabelPropagationPrefixes { - if strings.HasPrefix(k, prefix) { - continue base - } - } - mergedLabels[k] = v + var result map[string]string + if len(base) == 0 { + result = make(map[string]string) + } else { + result = maps.Clone(base) } - -overwrite: - for k, v := range overwrite { - for _, prefix := range b.ExcludeLabelPropagationPrefixes { - if strings.HasPrefix(k, prefix) { - continue overwrite - } - } - mergedLabels[k] = v + if len(overwrite) > 0 { + maps.Copy(result, overwrite) } - - return mergedLabels + maps.DeleteFunc(result, filter) + return result } -func (b *ResourceBuilder) mergeAnnotations(base, overwrite map[string]string) map[string]string { +func (b *ResourceBuilder) filterAndMergeAnnotations(base, overwrite map[string]string) map[string]string { if base == nil && overwrite == nil { return nil } - base = maps.Clone(base) - maps.Copy(base, overwrite) - return base + var result map[string]string + if len(base) == 0 { + result = make(map[string]string) + } else { + result = maps.Clone(base) + } + + for k, v := range overwrite { + if k == AnnotationKeyIntegrityHash { + continue + } + result[k] = v + } + + return result } diff --git a/controllers/actions.github.com/resourcebuilder_test.go b/controllers/actions.github.com/resourcebuilder_test.go index d08851173a..206e610748 100644 --- a/controllers/actions.github.com/resourcebuilder_test.go +++ b/controllers/actions.github.com/resourcebuilder_test.go @@ -113,7 +113,7 @@ func TestMetadataPropagation(t *testing.T) { assert.Equal(t, labelValueKubernetesPartOf, ephemeralRunnerSet.Labels[LabelKeyKubernetesPartOf]) assert.Equal(t, "runner-set", ephemeralRunnerSet.Labels[LabelKeyKubernetesComponent]) assert.Equal(t, autoscalingRunnerSet.Labels[LabelKeyKubernetesVersion], ephemeralRunnerSet.Labels[LabelKeyKubernetesVersion]) - assert.NotEmpty(t, ephemeralRunnerSet.Annotations[annotationKeyIntegrityHash]) + assert.NotEmpty(t, ephemeralRunnerSet.Annotations[AnnotationKeyIntegrityHash]) assert.Equal(t, autoscalingRunnerSet.Name, ephemeralRunnerSet.Labels[LabelKeyGitHubScaleSetName]) assert.Equal(t, autoscalingRunnerSet.Namespace, ephemeralRunnerSet.Labels[LabelKeyGitHubScaleSetNamespace]) assert.Equal(t, "", ephemeralRunnerSet.Labels[LabelKeyGitHubEnterprise]) @@ -130,7 +130,7 @@ func TestMetadataPropagation(t *testing.T) { assert.Equal(t, labelValueKubernetesPartOf, listener.Labels[LabelKeyKubernetesPartOf]) assert.Equal(t, "runner-scale-set-listener", listener.Labels[LabelKeyKubernetesComponent]) assert.Equal(t, autoscalingRunnerSet.Labels[LabelKeyKubernetesVersion], listener.Labels[LabelKeyKubernetesVersion]) - assert.NotEmpty(t, ephemeralRunnerSet.Annotations[annotationKeyIntegrityHash]) + assert.NotEmpty(t, ephemeralRunnerSet.Annotations[AnnotationKeyIntegrityHash]) assert.Equal(t, autoscalingRunnerSet.Name, listener.Labels[LabelKeyGitHubScaleSetName]) assert.Equal(t, autoscalingRunnerSet.Namespace, listener.Labels[LabelKeyGitHubScaleSetNamespace]) assert.Equal(t, "", listener.Labels[LabelKeyGitHubEnterprise]) @@ -221,7 +221,7 @@ func TestEphemeralRunnerSetProxySecretZIdentityHash(t *testing.T) { }) require.NoError(t, err) - actualHash := proxySecret.Annotations[annotationKeyIntegrityHash] + actualHash := proxySecret.Annotations[AnnotationKeyIntegrityHash] assert.NotEmpty(t, actualHash) assert.Equal(t, ephemeralRunnerSetProxySecretZIdentityHash(proxySecret), actualHash) @@ -313,7 +313,7 @@ func TestOwnershipRelationships(t *testing.T) { runnerScaleSetIDAnnotationKey: "1", AnnotationKeyGitHubRunnerGroupName: "test-group", AnnotationKeyGitHubRunnerScaleSetName: "test-scale-set", - annotationKeyIntegrityHash: "test-hash", + AnnotationKeyIntegrityHash: "test-hash", }, }, Spec: v1alpha1.AutoscalingRunnerSetSpec{ From cecd6388f6b06d85ac3d2e011c86d74edc0ba7f0 Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Tue, 16 Jun 2026 00:09:25 +0200 Subject: [PATCH 09/12] fix min/max runners --- .../v1alpha1/autoscalinglistener_types.go | 10 +++++----- controllers/actions.github.com/resourcebuilder.go | 7 ++++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go b/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go index b35705f8a3..20f4613549 100644 --- a/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go +++ b/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go @@ -31,7 +31,7 @@ type AutoscalingListenerSpec struct { GitHubConfigSecret string `json:"githubConfigSecret,omitempty"` // +required - // +kubebuilder:validation:Minimum=1 + // +kubebuilder:validation:Minimum:=1 RunnerScaleSetID int `json:"runnerScaleSetId,omitempty"` // +required @@ -43,13 +43,13 @@ type AutoscalingListenerSpec struct { // +required EphemeralRunnerSetName string `json:"ephemeralRunnerSetName,omitempty"` - // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Minimum:=0 // +required - MaxRunners int `json:"maxRunners,omitempty"` + MaxRunners int `json:"maxRunners"` - // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Minimum:=0 // +required - MinRunners int `json:"minRunners,omitempty"` + MinRunners int `json:"minRunners"` // +required Image string `json:"image,omitempty"` diff --git a/controllers/actions.github.com/resourcebuilder.go b/controllers/actions.github.com/resourcebuilder.go index 7c50a092e8..bdce614aaf 100644 --- a/controllers/actions.github.com/resourcebuilder.go +++ b/controllers/actions.github.com/resourcebuilder.go @@ -122,13 +122,14 @@ func (b *ResourceBuilder) newAutoscalingListener(autoscalingRunnerSet *v1alpha1. } effectiveMinRunners := 0 + if autoscalingRunnerSet.Spec.MinRunners != nil { + effectiveMinRunners = *autoscalingRunnerSet.Spec.MinRunners + } + effectiveMaxRunners := math.MaxInt32 if autoscalingRunnerSet.Spec.MaxRunners != nil { effectiveMaxRunners = *autoscalingRunnerSet.Spec.MaxRunners } - if autoscalingRunnerSet.Spec.MinRunners != nil { - effectiveMinRunners = *autoscalingRunnerSet.Spec.MinRunners - } spec := v1alpha1.AutoscalingListenerSpec{ GitHubConfigURL: autoscalingRunnerSet.Spec.GitHubConfigUrl, From 6b0e8992086ae9b58a106275248e8f847753c116 Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Wed, 17 Jun 2026 00:56:20 +0200 Subject: [PATCH 10/12] wip --- .../v1alpha1/autoscalinglistener_types.go | 18 +-- .../v1alpha1/autoscalingrunnerset_types.go | 20 +-- .../v1alpha1/ephemeralrunner_types.go | 9 +- ...tions.github.com_autoscalinglisteners.yaml | 10 -- ...ions.github.com_autoscalingrunnersets.yaml | 4 - .../actions.github.com_ephemeralrunners.yaml | 4 - ...ctions.github.com_ephemeralrunnersets.yaml | 4 - ...tions.github.com_autoscalinglisteners.yaml | 10 -- ...ions.github.com_autoscalingrunnersets.yaml | 4 - .../actions.github.com_ephemeralrunners.yaml | 4 - ...ctions.github.com_ephemeralrunnersets.yaml | 4 - ...tions.github.com_autoscalinglisteners.yaml | 10 -- ...ions.github.com_autoscalingrunnersets.yaml | 4 - .../actions.github.com_ephemeralrunners.yaml | 4 - ...ctions.github.com_ephemeralrunnersets.yaml | 4 - .../autoscalinglistener_controller.go | 32 ++-- .../autoscalingrunnerset_controller.go | 24 +-- .../autoscalingrunnerset_controller_test.go | 138 ++++++++++++++---- .../actions.github.com/resourcebuilder.go | 21 +++ controllers/actions.github.com/utils.go | 10 ++ 20 files changed, 194 insertions(+), 144 deletions(-) diff --git a/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go b/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go index 20f4613549..8f86bb4291 100644 --- a/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go +++ b/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go @@ -24,34 +24,34 @@ import ( // AutoscalingListenerSpec defines the desired state of AutoscalingListener type AutoscalingListenerSpec struct { - // +required + // +optional GitHubConfigURL string `json:"githubConfigUrl,omitempty"` - // +required + // +optional GitHubConfigSecret string `json:"githubConfigSecret,omitempty"` - // +required + // +optional // +kubebuilder:validation:Minimum:=1 RunnerScaleSetID int `json:"runnerScaleSetId,omitempty"` - // +required + // +optional AutoscalingRunnerSetNamespace string `json:"autoscalingRunnerSetNamespace,omitempty"` - // +required + // +optional AutoscalingRunnerSetName string `json:"autoscalingRunnerSetName,omitempty"` - // +required + // +optional EphemeralRunnerSetName string `json:"ephemeralRunnerSetName,omitempty"` // +kubebuilder:validation:Minimum:=0 - // +required + // +optional MaxRunners int `json:"maxRunners"` // +kubebuilder:validation:Minimum:=0 - // +required + // +optional MinRunners int `json:"minRunners"` - // +required + // +optional Image string `json:"image,omitempty"` // +optional diff --git a/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go b/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go index 23a3230cf6..804efc9c60 100644 --- a/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go +++ b/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go @@ -56,10 +56,10 @@ type AutoscalingRunnerSet struct { // AutoscalingRunnerSetSpec defines the desired state of AutoscalingRunnerSet type AutoscalingRunnerSetSpec struct { - // +required + // +optional GitHubConfigUrl string `json:"githubConfigUrl,omitempty"` - // +required + // +optional GitHubConfigSecret string `json:"githubConfigSecret,omitempty"` // +optional @@ -80,7 +80,7 @@ type AutoscalingRunnerSetSpec struct { // +optional VaultConfig *VaultConfig `json:"vaultConfig,omitempty"` - // +required + // +optional Template corev1.PodTemplateSpec `json:"template,omitempty"` // +optional @@ -346,20 +346,6 @@ const ( AutoscalingRunnerSetPhaseOutdated AutoscalingRunnerSetPhase = "Outdated" ) -func (ars *AutoscalingRunnerSet) Hash() string { - type data struct { - Spec *AutoscalingRunnerSetSpec - Labels map[string]string - } - - d := &data{ - Spec: ars.Spec.DeepCopy(), - Labels: ars.Labels, - } - - return hash.ComputeTemplateHash(d) -} - func (ars *AutoscalingRunnerSet) ListenerSpecHash() string { arsSpec := ars.Spec.DeepCopy() spec := arsSpec diff --git a/apis/actions.github.com/v1alpha1/ephemeralrunner_types.go b/apis/actions.github.com/v1alpha1/ephemeralrunner_types.go index 5ce38f8226..e53f257af3 100644 --- a/apis/actions.github.com/v1alpha1/ephemeralrunner_types.go +++ b/apis/actions.github.com/v1alpha1/ephemeralrunner_types.go @@ -105,17 +105,17 @@ func (er *EphemeralRunner) VaultProxy() *ProxyConfig { // EphemeralRunnerSpec defines the desired state of EphemeralRunner type EphemeralRunnerSpec struct { - // +required + // +optional GitHubConfigURL string `json:"githubConfigUrl,omitempty"` - // +required + // +optional GitHubConfigSecret string `json:"githubConfigSecret,omitempty"` // +optional GitHubServerTLS *TLSConfig `json:"githubServerTLS,omitempty"` - // +required - RunnerScaleSetID int `json:"runnerScaleSetId,omitempty"` + // +optional + RunnerScaleSetID int `json:"runnerScaleSetId"` // +optional Proxy *ProxyConfig `json:"proxy,omitempty"` @@ -129,6 +129,7 @@ type EphemeralRunnerSpec struct { // +optional EphemeralRunnerConfigSecretMetadata *ResourceMeta `json:"ephemeralRunnerConfigSecretMetadata,omitempty"` + // +optional corev1.PodTemplateSpec `json:",inline"` } diff --git a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_autoscalinglisteners.yaml b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_autoscalinglisteners.yaml index 9265298660..84e243f9a6 100644 --- a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_autoscalinglisteners.yaml +++ b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_autoscalinglisteners.yaml @@ -8802,16 +8802,6 @@ spec: It is used to identify which vault integration should be used to resolve secrets. type: string type: object - required: - - autoscalingRunnerSetName - - autoscalingRunnerSetNamespace - - ephemeralRunnerSetName - - githubConfigSecret - - githubConfigUrl - - image - - maxRunners - - minRunners - - runnerScaleSetId type: object status: description: AutoscalingListenerStatus defines the observed state of AutoscalingListener 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 122b52defe..b303ff1400 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 @@ -16541,10 +16541,6 @@ spec: It is used to identify which vault integration should be used to resolve secrets. type: string type: object - required: - - githubConfigSecret - - githubConfigUrl - - template type: object status: description: AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet diff --git a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunners.yaml b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunners.yaml index 0b413f417d..8248263e7d 100644 --- a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunners.yaml +++ b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunners.yaml @@ -8297,10 +8297,6 @@ spec: It is used to identify which vault integration should be used to resolve secrets. type: string type: object - required: - - githubConfigSecret - - githubConfigUrl - - runnerScaleSetId type: object status: description: EphemeralRunnerStatus defines the observed state of EphemeralRunner diff --git a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunnersets.yaml b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunnersets.yaml index 6ff8ca2dfa..04c274bbf2 100644 --- a/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunnersets.yaml +++ b/charts/gha-runner-scale-set-controller-experimental/crds/actions.github.com_ephemeralrunnersets.yaml @@ -8306,10 +8306,6 @@ spec: It is used to identify which vault integration should be used to resolve secrets. type: string type: object - required: - - githubConfigSecret - - githubConfigUrl - - runnerScaleSetId type: object patchID: description: PatchID is the unique identifier for the patch issued by the listener app diff --git a/charts/gha-runner-scale-set-controller/crds/actions.github.com_autoscalinglisteners.yaml b/charts/gha-runner-scale-set-controller/crds/actions.github.com_autoscalinglisteners.yaml index 9265298660..84e243f9a6 100644 --- a/charts/gha-runner-scale-set-controller/crds/actions.github.com_autoscalinglisteners.yaml +++ b/charts/gha-runner-scale-set-controller/crds/actions.github.com_autoscalinglisteners.yaml @@ -8802,16 +8802,6 @@ spec: It is used to identify which vault integration should be used to resolve secrets. type: string type: object - required: - - autoscalingRunnerSetName - - autoscalingRunnerSetNamespace - - ephemeralRunnerSetName - - githubConfigSecret - - githubConfigUrl - - image - - maxRunners - - minRunners - - runnerScaleSetId type: object status: description: AutoscalingListenerStatus defines the observed state of AutoscalingListener 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 122b52defe..b303ff1400 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 @@ -16541,10 +16541,6 @@ spec: It is used to identify which vault integration should be used to resolve secrets. type: string type: object - required: - - githubConfigSecret - - githubConfigUrl - - template type: object status: description: AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet diff --git a/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunners.yaml b/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunners.yaml index 0b413f417d..8248263e7d 100644 --- a/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunners.yaml +++ b/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunners.yaml @@ -8297,10 +8297,6 @@ spec: It is used to identify which vault integration should be used to resolve secrets. type: string type: object - required: - - githubConfigSecret - - githubConfigUrl - - runnerScaleSetId type: object status: description: EphemeralRunnerStatus defines the observed state of EphemeralRunner diff --git a/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunnersets.yaml b/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunnersets.yaml index 6ff8ca2dfa..04c274bbf2 100644 --- a/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunnersets.yaml +++ b/charts/gha-runner-scale-set-controller/crds/actions.github.com_ephemeralrunnersets.yaml @@ -8306,10 +8306,6 @@ spec: It is used to identify which vault integration should be used to resolve secrets. type: string type: object - required: - - githubConfigSecret - - githubConfigUrl - - runnerScaleSetId type: object patchID: description: PatchID is the unique identifier for the patch issued by the listener app diff --git a/config/crd/bases/actions.github.com_autoscalinglisteners.yaml b/config/crd/bases/actions.github.com_autoscalinglisteners.yaml index 9265298660..84e243f9a6 100644 --- a/config/crd/bases/actions.github.com_autoscalinglisteners.yaml +++ b/config/crd/bases/actions.github.com_autoscalinglisteners.yaml @@ -8802,16 +8802,6 @@ spec: It is used to identify which vault integration should be used to resolve secrets. type: string type: object - required: - - autoscalingRunnerSetName - - autoscalingRunnerSetNamespace - - ephemeralRunnerSetName - - githubConfigSecret - - githubConfigUrl - - image - - maxRunners - - minRunners - - runnerScaleSetId type: object status: description: AutoscalingListenerStatus defines the observed state of AutoscalingListener diff --git a/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml b/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml index 122b52defe..b303ff1400 100644 --- a/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml +++ b/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml @@ -16541,10 +16541,6 @@ spec: It is used to identify which vault integration should be used to resolve secrets. type: string type: object - required: - - githubConfigSecret - - githubConfigUrl - - template type: object status: description: AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet diff --git a/config/crd/bases/actions.github.com_ephemeralrunners.yaml b/config/crd/bases/actions.github.com_ephemeralrunners.yaml index 0b413f417d..8248263e7d 100644 --- a/config/crd/bases/actions.github.com_ephemeralrunners.yaml +++ b/config/crd/bases/actions.github.com_ephemeralrunners.yaml @@ -8297,10 +8297,6 @@ spec: It is used to identify which vault integration should be used to resolve secrets. type: string type: object - required: - - githubConfigSecret - - githubConfigUrl - - runnerScaleSetId type: object status: description: EphemeralRunnerStatus defines the observed state of EphemeralRunner diff --git a/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml b/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml index 6ff8ca2dfa..04c274bbf2 100644 --- a/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml +++ b/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml @@ -8306,10 +8306,6 @@ spec: It is used to identify which vault integration should be used to resolve secrets. type: string type: object - required: - - githubConfigSecret - - githubConfigUrl - - runnerScaleSetId type: object patchID: description: PatchID is the unique identifier for the patch issued by the listener app diff --git a/controllers/actions.github.com/autoscalinglistener_controller.go b/controllers/actions.github.com/autoscalinglistener_controller.go index dc8fc5bf97..431bdbdd6d 100644 --- a/controllers/actions.github.com/autoscalinglistener_controller.go +++ b/controllers/actions.github.com/autoscalinglistener_controller.go @@ -171,7 +171,7 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl. shouldUpdate = true } desiredAnnotations := r.filterAndMergeAnnotations(serviceAccount.Annotations, desiredServiceAccount.Annotations) - if !maps.Equal(serviceAccount.Annotations, desiredAnnotations) { + if !r.annotationsEqual(serviceAccount.Annotations, desiredAnnotations) { updatedServiceAccount.Annotations = desiredAnnotations shouldUpdate = true } @@ -215,7 +215,7 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl. shouldUpdate = true } desiredAnnotations := r.filterAndMergeAnnotations(listenerRole.Annotations, desiredRole.Annotations) - if !maps.Equal(listenerRole.Annotations, desiredAnnotations) { + if !r.annotationsEqual(listenerRole.Annotations, desiredAnnotations) { updatedRole.Annotations = desiredAnnotations shouldUpdate = true } @@ -258,7 +258,7 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl. shouldUpdate = true } desiredAnnotations := r.filterAndMergeAnnotations(listenerRoleBinding.Annotations, desiredRoleBinding.Annotations) - if !maps.Equal(listenerRoleBinding.Annotations, desiredAnnotations) { + if !r.annotationsEqual(listenerRoleBinding.Annotations, desiredAnnotations) { updatedRoleBinding.Annotations = desiredAnnotations shouldUpdate = true } @@ -314,7 +314,7 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl. shouldUpdate = true } desiredAnnotations := r.filterAndMergeAnnotations(proxySecret.Annotations, desiredListenerProxy.Annotations) - if !maps.Equal(proxySecret.Annotations, desiredAnnotations) { + if !r.annotationsEqual(proxySecret.Annotations, desiredAnnotations) { updatedProxySecret.Annotations = desiredAnnotations shouldUpdate = true } @@ -401,7 +401,7 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl. shouldUpdate = true } desiredAnnotations := r.filterAndMergeAnnotations(listenerConfigSecret.Annotations, desiredSecret.Annotations) - if !maps.Equal(listenerConfigSecret.Annotations, desiredAnnotations) { + if !r.annotationsEqual(listenerConfigSecret.Annotations, desiredAnnotations) { updatedSecret.Annotations = desiredAnnotations shouldUpdate = true } @@ -467,9 +467,17 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl. return ctrl.Result{}, err } - shouldReCreate := desiredPod.Annotations[AnnotationKeyIntegrityHash] != listenerPod.Annotations[AnnotationKeyIntegrityHash] - if shouldReCreate { - log.Info("Listener pod dependency changed, recreating listener pod") + if desiredPod.Annotations[AnnotationKeyIntegrityHash] != listenerPod.Annotations[AnnotationKeyIntegrityHash] { + // Since the pod is controlled by a pod controller, we tag the pod with integrity hash. + // If the integrity hash is changed, that means the new spec is different. Keep in mind, the tagged hash + // is created by hashing only the fields this controller sets. + log.Info( + "Listener pod dependency changed, recreating listener pod", + "desiredSpec", + mustJSON(desiredPod.Spec), + "currentSpec", + mustJSON(listenerPod.Spec), + ) if err := r.deleteListenerPod(ctx, &autoscalingListener, &listenerPod, log); err != nil { return ctrl.Result{}, err } @@ -486,7 +494,7 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl. shouldUpdate = true } desiredAnnotations := r.filterAndMergeAnnotations(listenerPod.Annotations, desiredPod.Annotations) - if !maps.Equal(listenerPod.Annotations, desiredAnnotations) { + if !r.annotationsEqual(listenerPod.Annotations, desiredAnnotations) { updatedPod.Annotations = desiredAnnotations shouldUpdate = true } @@ -519,7 +527,11 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl. return ctrl.Result{}, err } - log.Info("Creating listener pod", "namespace", desiredPod.Namespace, "name", desiredPod.Name) + log.Info( + "Creating listener pod", + "namespace", desiredPod.Namespace, + "name", desiredPod.Name, + ) if err := r.Create(ctx, desiredPod); err != nil { log.Error(err, "Unable to create listener pod", "namespace", desiredPod.Namespace, "name", desiredPod.Name) return ctrl.Result{}, err diff --git a/controllers/actions.github.com/autoscalingrunnerset_controller.go b/controllers/actions.github.com/autoscalingrunnerset_controller.go index a9cc7506c5..a392ac813c 100644 --- a/controllers/actions.github.com/autoscalingrunnerset_controller.go +++ b/controllers/actions.github.com/autoscalingrunnerset_controller.go @@ -142,13 +142,11 @@ func (r *AutoscalingRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl } // Something has changed, we need to re-apply the pending phase and change hash annotation to trigger the update of runner scale set and listener. - if targetHash := autoscalingRunnerSet.Hash(); autoscalingRunnerSet.Annotations[AnnotationKeyIntegrityHash] != targetHash { - // TODO: apply the version label + if targetHash := autoscalingRunnerSetIntegrityHash(&autoscalingRunnerSet); autoscalingRunnerSet.Annotations[AnnotationKeyIntegrityHash] != targetHash { original := autoscalingRunnerSet.DeepCopy() if autoscalingRunnerSet.Annotations == nil { autoscalingRunnerSet.Annotations = map[string]string{} } - autoscalingRunnerSet.Annotations[AnnotationKeyIntegrityHash] = targetHash if err := r.Patch(ctx, &autoscalingRunnerSet, client.MergeFrom(original)); err != nil { log.Error(err, "Failed to update autoscaling runner set with new change hash and pending phase") return ctrl.Result{}, err @@ -291,7 +289,8 @@ func (r *AutoscalingRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl return ctrl.Result{}, nil } - if ephemeralRunnerSet.Annotations[AnnotationKeyIntegrityHash] != desired.Annotations[AnnotationKeyIntegrityHash] { + integrityDiff := desired.Annotations[AnnotationKeyIntegrityHash] != ephemeralRunnerSetIntegrityHash(&ephemeralRunnerSet) + if integrityDiff { original := ephemeralRunnerSet.DeepCopy() ephemeralRunnerSet.Spec.EphemeralRunnerMetadata = desired.Spec.EphemeralRunnerMetadata ephemeralRunnerSet.Spec.EphemeralRunnerSpec = desired.Spec.EphemeralRunnerSpec @@ -310,7 +309,7 @@ func (r *AutoscalingRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl ephemeralRunnerMetadataModified := !cmp.Equal(ephemeralRunnerSet.Spec.EphemeralRunnerMetadata, desired.Spec.EphemeralRunnerMetadata) ephemeralRunnerLabelsModified := !maps.Equal(ephemeralRunnerSet.Labels, desired.Labels) - ephemeralRunnerAnnotationsModified := !maps.Equal(ephemeralRunnerSet.Annotations, desired.Annotations) + ephemeralRunnerAnnotationsModified := !r.annotationsEqual(ephemeralRunnerSet.Annotations, desired.Annotations) if ephemeralRunnerLabelsModified || ephemeralRunnerAnnotationsModified || ephemeralRunnerMetadataModified { original := ephemeralRunnerSet.DeepCopy() @@ -358,14 +357,17 @@ func (r *AutoscalingRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl } if !cmp.Equal(listener.Spec, desired.Spec) || - !cmp.Equal(listener.Labels, desired.Labels) || - !cmp.Equal(listener.Annotations, desired.Annotations) { - log.Info("Deleting AutoscalingListener to re-create with updated spec") - if err := r.Delete(ctx, &listener); err != nil { - log.Error(err, "Failed to delete AutoscalingListener for re-creation") + !maps.Equal(listener.Labels, desired.Labels) || + !r.annotationsEqual(listener.Annotations, desired.Annotations) { + log.Info("Updating listener") + listener.Spec = desired.Spec + listener.Annotations = r.filterAndMergeAnnotations(listener.Annotations, desired.Annotations) + listener.Labels = r.filterAndMergeLabels(listener.Labels, desired.Labels) + if err := r.Update(ctx, &listener); err != nil { + log.Error(err, "Failed to update AutoscalingListener with new spec") return ctrl.Result{}, err } - log.Info("Deleted AutoscalingListener, will re-create on next reconcile") + log.Info("Successfully updated AutoscalingListener with new spec") return ctrl.Result{}, nil } } diff --git a/controllers/actions.github.com/autoscalingrunnerset_controller_test.go b/controllers/actions.github.com/autoscalingrunnerset_controller_test.go index d529a1ab17..fe4c184249 100644 --- a/controllers/actions.github.com/autoscalingrunnerset_controller_test.go +++ b/controllers/actions.github.com/autoscalingrunnerset_controller_test.go @@ -461,7 +461,14 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { listener := new(v1alpha1.AutoscalingListener) Eventually( func() error { - return k8sClient.Get(ctx, client.ObjectKey{Name: scaleSetListenerName(autoscalingRunnerSet), Namespace: autoscalingRunnerSet.Namespace}, listener) + return k8sClient.Get( + ctx, + client.ObjectKey{ + Name: scaleSetListenerName(autoscalingRunnerSet), + Namespace: autoscalingRunnerSet.Namespace, + }, + listener, + ) }, autoscalingRunnerSetTestTimeout, autoscalingRunnerSetTestInterval, @@ -472,13 +479,21 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { runnerSet := new(v1alpha1.EphemeralRunnerSet) Eventually( func() error { - return k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingRunnerSet.Name, Namespace: autoscalingRunnerSet.Namespace}, runnerSet) + return k8sClient.Get( + ctx, + client.ObjectKey{ + Name: autoscalingRunnerSet.Name, + Namespace: autoscalingRunnerSet.Namespace, + }, + runnerSet, + ) }, autoscalingRunnerSetTestTimeout, autoscalingRunnerSetTestInterval, ).Should(Succeed(), "EphemeralRunnerSet should be created") originalRunnerSetUID := runnerSet.UID originalRunnerSetHash := runnerSet.Annotations[AnnotationKeyIntegrityHash] + originalResourceVersion := runnerSet.ResourceVersion patched := autoscalingRunnerSet.DeepCopy() patched.Spec.Template.Spec.Containers[0].Image = "ghcr.io/actions/runner:updated" @@ -492,7 +507,8 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { g.Expect(err).NotTo(HaveOccurred(), "failed to get EphemeralRunnerSet") g.Expect(current.UID).To(Equal(originalRunnerSetUID), "EphemeralRunnerSet should be updated in place") g.Expect(current.Spec.EphemeralRunnerSpec.PodTemplateSpec.Spec.Containers[0].Image).To(Equal("ghcr.io/actions/runner:updated")) - g.Expect(current.Annotations[AnnotationKeyIntegrityHash]).NotTo(Equal(originalRunnerSetHash), "EphemeralRunnerSet spec hash should change") + g.Expect(current.Annotations[AnnotationKeyIntegrityHash]).To(Equal(originalRunnerSetHash), "EphemeralRunnerSet hash integrity key should not be modified") + g.Expect(current.ResourceVersion).NotTo(Equal(originalResourceVersion), "EphemeralRunnerSet ResourceVersion should change after update") }, autoscalingRunnerSetTestTimeout, autoscalingRunnerSetTestInterval, @@ -504,34 +520,50 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { err := k8sClient.Get(ctx, client.ObjectKey{Name: scaleSetListenerName(autoscalingRunnerSet), Namespace: autoscalingRunnerSet.Namespace}, current) g.Expect(err).NotTo(HaveOccurred(), "failed to get Listener") g.Expect(current.UID).To(Equal(originalListenerUID), "Listener should not be recreated") - g.Expect(current.ResourceVersion).To(Equal(originalListenerResourceVersion), "Listener should not be updated") + g.Expect(current.ResourceVersion).To(Equal(originalListenerResourceVersion), "Listener ResourceVersion should not change after update") }, - time.Second*5, + autoscalingRunnerSetTestTimeout, autoscalingRunnerSetTestInterval, ).Should(Succeed()) }) - It("recreates only the Listener when max runners changes", func() { + It("Updates only the Listener when max runners changes", func() { listener := new(v1alpha1.AutoscalingListener) Eventually( func() error { - return k8sClient.Get(ctx, client.ObjectKey{Name: scaleSetListenerName(autoscalingRunnerSet), Namespace: autoscalingRunnerSet.Namespace}, listener) + return k8sClient.Get( + ctx, + client.ObjectKey{ + Name: scaleSetListenerName(autoscalingRunnerSet), + Namespace: autoscalingRunnerSet.Namespace, + }, + listener, + ) }, autoscalingRunnerSetTestTimeout, autoscalingRunnerSetTestInterval, ).Should(Succeed(), "Listener should be created") originalListenerUID := listener.UID + originalListenerResourceVersion := listener.ResourceVersion + originalListenerIntegrityHash := listener.Annotations[AnnotationKeyIntegrityHash] runnerSet := new(v1alpha1.EphemeralRunnerSet) Eventually( func() error { - return k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingRunnerSet.Name, Namespace: autoscalingRunnerSet.Namespace}, runnerSet) + return k8sClient.Get( + ctx, + client.ObjectKey{ + Name: autoscalingRunnerSet.Name, + Namespace: autoscalingRunnerSet.Namespace, + }, + runnerSet, + ) }, autoscalingRunnerSetTestTimeout, autoscalingRunnerSetTestInterval, ).Should(Succeed(), "EphemeralRunnerSet should be created") - originalRunnerSetUID := runnerSet.UID - originalRunnerSetHash := runnerSet.Annotations[AnnotationKeyIntegrityHash] + originalERSRunnerSetUID := runnerSet.UID + originalERSResourceVersion := runnerSet.ResourceVersion patched := autoscalingRunnerSet.DeepCopy() max := 20 @@ -544,8 +576,10 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { current := new(v1alpha1.AutoscalingListener) err := k8sClient.Get(ctx, client.ObjectKey{Name: scaleSetListenerName(autoscalingRunnerSet), Namespace: autoscalingRunnerSet.Namespace}, current) g.Expect(err).NotTo(HaveOccurred(), "failed to get Listener") - g.Expect(current.UID).NotTo(Equal(originalListenerUID), "Listener should be recreated") + g.Expect(current.UID).To(Equal(originalListenerUID), "Listener should be updated") + g.Expect(current.Annotations[AnnotationKeyIntegrityHash]).To(Equal(originalListenerIntegrityHash), "Listener hash integrity key should not be modified") g.Expect(current.Spec.MaxRunners).To(Equal(max)) + g.Expect(current.ResourceVersion).NotTo(Equal(originalListenerResourceVersion), "Listener ResourceVersion should change after update") }, autoscalingRunnerSetTestTimeout, autoscalingRunnerSetTestInterval, @@ -556,8 +590,8 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { current := new(v1alpha1.EphemeralRunnerSet) err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingRunnerSet.Name, Namespace: autoscalingRunnerSet.Namespace}, current) g.Expect(err).NotTo(HaveOccurred(), "failed to get EphemeralRunnerSet") - g.Expect(current.UID).To(Equal(originalRunnerSetUID), "EphemeralRunnerSet should not be recreated") - g.Expect(current.Annotations[AnnotationKeyIntegrityHash]).To(Equal(originalRunnerSetHash), "EphemeralRunnerSet spec should not change") + g.Expect(current.UID).To(Equal(originalERSRunnerSetUID), "EphemeralRunnerSet should not be recreated") + g.Expect(current.ResourceVersion).To(Equal(originalERSResourceVersion), "EphemeralRunnerSet spec should not change") }, time.Second*5, autoscalingRunnerSetTestInterval, @@ -568,7 +602,14 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { runnerSet := new(v1alpha1.EphemeralRunnerSet) Eventually( func() (string, error) { - err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingRunnerSet.Name, Namespace: autoscalingRunnerSet.Namespace}, runnerSet) + err := k8sClient.Get( + ctx, + client.ObjectKey{ + Name: autoscalingRunnerSet.Name, + Namespace: autoscalingRunnerSet.Namespace, + }, + runnerSet, + ) if err != nil { return "", err } @@ -586,7 +627,14 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { Eventually( func() (string, error) { current := new(v1alpha1.EphemeralRunnerSet) - err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingRunnerSet.Name, Namespace: autoscalingRunnerSet.Namespace}, current) + err := k8sClient.Get( + ctx, + client.ObjectKey{ + Name: autoscalingRunnerSet.Name, + Namespace: autoscalingRunnerSet.Namespace, + }, + current, + ) if err != nil { return "", err } @@ -601,7 +649,14 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { runnerSet := new(v1alpha1.EphemeralRunnerSet) Eventually( func() (string, error) { - err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingRunnerSet.Name, Namespace: autoscalingRunnerSet.Namespace}, runnerSet) + err := k8sClient.Get( + ctx, + client.ObjectKey{ + Name: autoscalingRunnerSet.Name, + Namespace: autoscalingRunnerSet.Namespace, + }, + runnerSet, + ) if err != nil { return "", err } @@ -614,6 +669,8 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { patched := autoscalingRunnerSet.DeepCopy() patched.Spec.EphemeralRunnerSetMetadata.Annotations["arc.test/metadata-annotation"] = "updated" patched.Spec.EphemeralRunnerSetMetadata.Annotations["arc.test/new-metadata-annotation"] = "added" + originalERSIntegrityHash := runnerSet.Annotations[AnnotationKeyIntegrityHash] + patched.Spec.EphemeralRunnerSetMetadata.Annotations[AnnotationKeyIntegrityHash] = "must-not-be-modified" err := k8sClient.Patch(ctx, patched, client.MergeFrom(autoscalingRunnerSet)) Expect(err).NotTo(HaveOccurred(), "failed to patch AutoScalingRunnerSet EphemeralRunnerSet metadata") @@ -624,6 +681,7 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { g.Expect(err).NotTo(HaveOccurred(), "failed to get EphemeralRunnerSet") g.Expect(current.Annotations["arc.test/metadata-annotation"]).To(Equal("updated")) g.Expect(current.Annotations["arc.test/new-metadata-annotation"]).To(Equal("added")) + g.Expect(current.Annotations[AnnotationKeyIntegrityHash]).To(Equal(originalERSIntegrityHash), "EphemeralRunnerSet hash integrity key should not be modified") }, autoscalingRunnerSetTestTimeout, autoscalingRunnerSetTestInterval, @@ -634,7 +692,14 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { runnerSet := new(v1alpha1.EphemeralRunnerSet) Eventually( func(g Gomega) { - err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingRunnerSet.Name, Namespace: autoscalingRunnerSet.Namespace}, runnerSet) + err := k8sClient.Get( + ctx, + client.ObjectKey{ + Name: autoscalingRunnerSet.Name, + Namespace: autoscalingRunnerSet.Namespace, + }, + runnerSet, + ) g.Expect(err).NotTo(HaveOccurred(), "failed to get EphemeralRunnerSet") g.Expect(runnerSet.Spec.EphemeralRunnerMetadata).NotTo(BeNil()) g.Expect(runnerSet.Spec.EphemeralRunnerMetadata.Labels["arc.test/runner-metadata-label"]).To(Equal("initial")) @@ -719,22 +784,38 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { listener := new(v1alpha1.AutoscalingListener) Eventually( func() error { - return k8sClient.Get(ctx, client.ObjectKey{Name: scaleSetListenerName(autoscalingRunnerSet), Namespace: autoscalingRunnerSet.Namespace}, listener) + return k8sClient.Get( + ctx, + client.ObjectKey{ + Name: scaleSetListenerName(autoscalingRunnerSet), + Namespace: autoscalingRunnerSet.Namespace, + }, + listener, + ) }, autoscalingRunnerSetTestTimeout, autoscalingRunnerSetTestInterval, ).Should(Succeed(), "Listener should be created") originalListenerUID := listener.UID + originalListenerIntegrityHash := listener.Annotations[AnnotationKeyIntegrityHash] runnerSet := new(v1alpha1.EphemeralRunnerSet) Eventually( func() error { - return k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingRunnerSet.Name, Namespace: autoscalingRunnerSet.Namespace}, runnerSet) + return k8sClient.Get( + ctx, + client.ObjectKey{ + Name: autoscalingRunnerSet.Name, + Namespace: autoscalingRunnerSet.Namespace, + }, + runnerSet, + ) }, autoscalingRunnerSetTestTimeout, autoscalingRunnerSetTestInterval, ).Should(Succeed(), "EphemeralRunnerSet should be created") - originalRunnerSetUID := runnerSet.UID + originalEphemeralRunnerSetUID := runnerSet.UID + originalEphemeralRunnerSetIntegrityHash := runnerSet.Annotations[AnnotationKeyIntegrityHash] patched := autoscalingRunnerSet.DeepCopy() patched.Spec.GitHubConfigSecret = updatedSecret.Name @@ -757,8 +838,9 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { current := new(v1alpha1.EphemeralRunnerSet) err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingRunnerSet.Name, Namespace: autoscalingRunnerSet.Namespace}, current) g.Expect(err).NotTo(HaveOccurred(), "failed to get EphemeralRunnerSet") - g.Expect(current.UID).To(Equal(originalRunnerSetUID), "EphemeralRunnerSet should be updated in place") + g.Expect(current.UID).To(Equal(originalEphemeralRunnerSetUID), "EphemeralRunnerSet should be updated in place") g.Expect(current.Spec.EphemeralRunnerSpec.GitHubConfigSecret).To(Equal(updatedSecret.Name)) + g.Expect(current.Annotations[AnnotationKeyIntegrityHash]).To(Equal(originalEphemeralRunnerSetIntegrityHash), "EphemeralRunnerSet hash integrity key should not be modified") }, autoscalingRunnerSetTestTimeout, autoscalingRunnerSetTestInterval, @@ -769,8 +851,9 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { current := new(v1alpha1.AutoscalingListener) err := k8sClient.Get(ctx, client.ObjectKey{Name: scaleSetListenerName(autoscalingRunnerSet), Namespace: autoscalingRunnerSet.Namespace}, current) g.Expect(err).NotTo(HaveOccurred(), "failed to get Listener") - g.Expect(current.UID).NotTo(Equal(originalListenerUID), "Listener should be recreated") - g.Expect(current.Spec.GitHubConfigSecret).To(Equal(updatedSecret.Name)) + g.Expect(current.UID).To(Equal(originalListenerUID), "Listener should be updated in place") + g.Expect(updatedSecret.Name).To(Equal(current.Spec.GitHubConfigSecret)) + g.Expect(current.Annotations[AnnotationKeyIntegrityHash]).To(Equal(originalListenerIntegrityHash), "Listener hash integrity key should not be modified") }, autoscalingRunnerSetTestTimeout, autoscalingRunnerSetTestInterval, @@ -1211,10 +1294,11 @@ var _ = Describe("Test AutoscalingController creation failures", Ordered, func() }, }, Spec: v1alpha1.AutoscalingRunnerSetSpec{ - GitHubConfigUrl: "https://github.com/owner/repo", - MaxRunners: &max, - MinRunners: &min, - RunnerGroup: "testgroup", + GitHubConfigUrl: "https://github.com/owner/repo", + GitHubConfigSecret: "secret1", + MaxRunners: &max, + MinRunners: &min, + RunnerGroup: "testgroup", Template: corev1.PodTemplateSpec{ Spec: corev1.PodSpec{ Containers: []corev1.Container{ diff --git a/controllers/actions.github.com/resourcebuilder.go b/controllers/actions.github.com/resourcebuilder.go index bdce614aaf..e08be51a41 100644 --- a/controllers/actions.github.com/resourcebuilder.go +++ b/controllers/actions.github.com/resourcebuilder.go @@ -115,6 +115,10 @@ func (b *ResourceBuilder) setControllerReference(owner client.Object, object cli return ctrl.SetControllerReference(owner, object, b.Scheme) } +func autoscalingRunnerSetIntegrityHash(ars *v1alpha1.AutoscalingRunnerSet) string { + return hash.ComputeTemplateHash(&ars.Spec) +} + func (b *ResourceBuilder) newAutoscalingListener(autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, ephemeralRunnerSet *v1alpha1.EphemeralRunnerSet, namespace, image string, imagePullSecrets []corev1.LocalObjectReference) (*v1alpha1.AutoscalingListener, error) { runnerScaleSetID, err := strconv.Atoi(autoscalingRunnerSet.Annotations[runnerScaleSetIDAnnotationKey]) if err != nil { @@ -1144,3 +1148,20 @@ func (b *ResourceBuilder) filterAndMergeAnnotations(base, overwrite map[string]s return result } + +// compareAnnotations compares two maps of annotations, ignoring the integrity hash annotation. +func (b *ResourceBuilder) annotationsEqual(m1, m2 map[string]string) bool { + if len(m1) != len(m2) { + return false + } + + for k, v1 := range m1 { + if k == AnnotationKeyIntegrityHash { + continue + } + if v2, ok := m2[k]; !ok || v1 != v2 { + return false + } + } + return true +} diff --git a/controllers/actions.github.com/utils.go b/controllers/actions.github.com/utils.go index a77b24ba17..da87eb7b21 100644 --- a/controllers/actions.github.com/utils.go +++ b/controllers/actions.github.com/utils.go @@ -1,6 +1,8 @@ package actionsgithubcom import ( + "encoding/json" + "k8s.io/apimachinery/pkg/util/rand" ) @@ -25,3 +27,11 @@ func RandStringRunes(n int) string { } return string(b) } + +func mustJSON(v any) string { + val, err := json.Marshal(v) + if err != nil { + panic(err) + } + return string(val) +} From a3bad10acf769c7373fac64039338a1605747422 Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Wed, 17 Jun 2026 14:07:12 +0200 Subject: [PATCH 11/12] wip --- .../autoscalingrunnerset_controller.go | 1 + .../autoscalingrunnerset_controller_test.go | 105 ------------------ 2 files changed, 1 insertion(+), 105 deletions(-) diff --git a/controllers/actions.github.com/autoscalingrunnerset_controller.go b/controllers/actions.github.com/autoscalingrunnerset_controller.go index a392ac813c..453ae2bdd1 100644 --- a/controllers/actions.github.com/autoscalingrunnerset_controller.go +++ b/controllers/actions.github.com/autoscalingrunnerset_controller.go @@ -147,6 +147,7 @@ func (r *AutoscalingRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl if autoscalingRunnerSet.Annotations == nil { autoscalingRunnerSet.Annotations = map[string]string{} } + autoscalingRunnerSet.Annotations[AnnotationKeyIntegrityHash] = targetHash if err := r.Patch(ctx, &autoscalingRunnerSet, client.MergeFrom(original)); err != nil { log.Error(err, "Failed to update autoscaling runner set with new change hash and pending phase") return ctrl.Result{}, err diff --git a/controllers/actions.github.com/autoscalingrunnerset_controller_test.go b/controllers/actions.github.com/autoscalingrunnerset_controller_test.go index fe4c184249..e4d227eee5 100644 --- a/controllers/actions.github.com/autoscalingrunnerset_controller_test.go +++ b/controllers/actions.github.com/autoscalingrunnerset_controller_test.go @@ -920,111 +920,6 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { }) }) - Context("When updating an AutoscalingRunnerSet with running or pending jobs", func() { - It("It should wait for running and pending jobs to finish before applying the update.", func() { - // Wait till the listener is created - listener := new(v1alpha1.AutoscalingListener) - Eventually( - func() error { - return k8sClient.Get(ctx, client.ObjectKey{Name: scaleSetListenerName(autoscalingRunnerSet), Namespace: autoscalingRunnerSet.Namespace}, listener) - }, - autoscalingRunnerSetTestTimeout, - autoscalingRunnerSetTestInterval, - ).Should(Succeed(), "Listener should be created") - - // Wait till the ephemeral runner set is created - Eventually( - func() (int, error) { - runnerSetList := new(v1alpha1.EphemeralRunnerSetList) - err := k8sClient.List(ctx, runnerSetList, client.InNamespace(autoscalingRunnerSet.Namespace)) - if err != nil { - return 0, err - } - - return len(runnerSetList.Items), nil - }, - autoscalingRunnerSetTestTimeout, - autoscalingRunnerSetTestInterval, - ).Should(BeEquivalentTo(1), "Only one EphemeralRunnerSet should be created") - - runnerSetList := new(v1alpha1.EphemeralRunnerSetList) - err := k8sClient.List(ctx, runnerSetList, client.InNamespace(autoscalingRunnerSet.Namespace)) - Expect(err).NotTo(HaveOccurred(), "failed to list EphemeralRunnerSet") - - // Emulate running and pending jobs - runnerSet := runnerSetList.Items[0] - activeRunnerSet := runnerSet.DeepCopy() - activeRunnerSet.Status.CurrentReplicas = 6 - activeRunnerSet.Status.FailedEphemeralRunners = 1 - activeRunnerSet.Status.RunningEphemeralRunners = 2 - activeRunnerSet.Status.PendingEphemeralRunners = 3 - - desiredStatus := v1alpha1.AutoscalingRunnerSetStatus{ - CurrentRunners: activeRunnerSet.Status.CurrentReplicas, - Phase: v1alpha1.AutoscalingRunnerSetPhaseRunning, - PendingEphemeralRunners: activeRunnerSet.Status.PendingEphemeralRunners, - RunningEphemeralRunners: activeRunnerSet.Status.RunningEphemeralRunners, - FailedEphemeralRunners: activeRunnerSet.Status.FailedEphemeralRunners, - } - - err = k8sClient.Status().Patch(ctx, activeRunnerSet, client.MergeFrom(&runnerSet)) - Expect(err).NotTo(HaveOccurred(), "Failed to patch runner set status") - - Eventually( - func() (v1alpha1.AutoscalingRunnerSetStatus, error) { - updated := new(v1alpha1.AutoscalingRunnerSet) - err := k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingRunnerSet.Name, Namespace: autoscalingRunnerSet.Namespace}, updated) - if err != nil { - return v1alpha1.AutoscalingRunnerSetStatus{}, fmt.Errorf("failed to get AutoScalingRunnerSet: %w", err) - } - return updated.Status, nil - }, - autoscalingRunnerSetTestTimeout, - autoscalingRunnerSetTestInterval, - ).Should(BeEquivalentTo(desiredStatus), "AutoScalingRunnerSet status should be updated") - - // Patch the AutoScalingRunnerSet image which should trigger - // the recreation of the Listener and EphemeralRunnerSet - patched := autoscalingRunnerSet.DeepCopy() - if patched.Annotations == nil { - patched.Annotations = make(map[string]string) - } - patched.Annotations[AnnotationKeyIntegrityHash] = "testgroup2" - patched.Spec.Template.Spec = corev1.PodSpec{ - Containers: []corev1.Container{ - { - Name: "runner", - Image: "ghcr.io/actions/abcd:1.1.1", - }, - }, - } - err = k8sClient.Patch(ctx, patched, client.MergeFrom(autoscalingRunnerSet)) - Expect(err).NotTo(HaveOccurred(), "failed to patch AutoScalingRunnerSet") - autoscalingRunnerSet = patched.DeepCopy() - - // The EphemeralRunnerSet should not be recreated - Consistently( - func() (string, error) { - runnerSetList := new(v1alpha1.EphemeralRunnerSetList) - err := k8sClient.List(ctx, runnerSetList, client.InNamespace(autoscalingRunnerSet.Namespace)) - Expect(err).NotTo(HaveOccurred(), "failed to fetch AutoScalingRunnerSet") - return runnerSetList.Items[0].Name, nil - }, - autoscalingRunnerSetTestTimeout, - autoscalingRunnerSetTestInterval, - ).Should(Equal(activeRunnerSet.Name), "The EphemeralRunnerSet should not be recreated") - - // The listener should not be recreated - Consistently( - func() error { - return k8sClient.Get(ctx, client.ObjectKey{Name: scaleSetListenerName(autoscalingRunnerSet), Namespace: autoscalingRunnerSet.Namespace}, listener) - }, - autoscalingRunnerSetTestTimeout, - autoscalingRunnerSetTestInterval, - ).ShouldNot(Succeed(), "Listener should not be recreated") - }) - }) - It("Should update Status on EphemeralRunnerSet status Update", func() { ars := new(v1alpha1.AutoscalingRunnerSet) Eventually( From ab4ebe731e4e0d556ea3f29976c4bcb19fe88a69 Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Mon, 22 Jun 2026 01:01:53 +0200 Subject: [PATCH 12/12] Fix tag --- .../v1alpha1/autoscalinglistener_types.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go b/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go index 8f86bb4291..990a6196dd 100644 --- a/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go +++ b/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go @@ -31,7 +31,7 @@ type AutoscalingListenerSpec struct { GitHubConfigSecret string `json:"githubConfigSecret,omitempty"` // +optional - // +kubebuilder:validation:Minimum:=1 + // +kubebuilder:validation:Minimum=1 RunnerScaleSetID int `json:"runnerScaleSetId,omitempty"` // +optional @@ -43,11 +43,11 @@ type AutoscalingListenerSpec struct { // +optional EphemeralRunnerSetName string `json:"ephemeralRunnerSetName,omitempty"` - // +kubebuilder:validation:Minimum:=0 + // +kubebuilder:validation:Minimum=0 // +optional MaxRunners int `json:"maxRunners"` - // +kubebuilder:validation:Minimum:=0 + // +kubebuilder:validation:Minimum=0 // +optional MinRunners int `json:"minRunners"`