From f6a3d738de018c7f5163f7d8924c3ceabc80f7dd Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Tue, 14 Jul 2026 19:31:11 +0200 Subject: [PATCH] Use metrics to display runner statuses instead of status field for EphemeralRunnerSet and AutoscalingRunnerSet (#4557) --- .../v1alpha1/autoscalinglistener_types.go | 5 +- .../v1alpha1/autoscalingrunnerset_types.go | 12 - .../v1alpha1/ephemeralrunnerset_types.go | 14 - ...ions.github.com_autoscalingrunnersets.yaml | 8 - ...ctions.github.com_ephemeralrunnersets.yaml | 24 -- ...ions.github.com_autoscalingrunnersets.yaml | 8 - ...ctions.github.com_ephemeralrunnersets.yaml | 24 -- ...ions.github.com_autoscalingrunnersets.yaml | 8 - ...ctions.github.com_ephemeralrunnersets.yaml | 24 -- .../autoscalingrunnerset_controller.go | 29 +- .../autoscalingrunnerset_controller_test.go | 116 +------- .../ephemeralrunner_controller.go | 96 ++++++- .../ephemeralrunnerset_controller.go | 37 +-- .../ephemeralrunnerset_controller_test.go | 269 ++++++++++++++++-- .../actions.github.com/metrics/metrics.go | 44 ++- github/actions/testdata/intermediate.crt | 32 +-- github/actions/testdata/leaf.crt | 34 +-- github/actions/testdata/leaf.key | 52 ++-- github/actions/testdata/rootCA.crt | 34 +-- github/actions/testdata/server.crt | 34 +-- github/actions/testdata/server.key | 52 ++-- main.go | 2 +- 22 files changed, 509 insertions(+), 449 deletions(-) diff --git a/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go b/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go index 21464637..fc4697c8 100644 --- a/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go +++ b/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go @@ -42,11 +42,11 @@ type AutoscalingListenerSpec struct { // +optional EphemeralRunnerSetName string `json:"ephemeralRunnerSetName,omitempty"` - // +kubebuilder:validation:Minimum:=0 + // +kubebuilder:validation:Minimum=0 // +optional MaxRunners int `json:"maxRunners,omitempty"` - // +kubebuilder:validation:Minimum:=0 + // +kubebuilder:validation:Minimum=0 // +optional MinRunners int `json:"minRunners,omitempty"` @@ -109,6 +109,7 @@ type AutoscalingListener struct { 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 { diff --git a/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go b/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go index a641d6c5..7f16eb3b 100644 --- a/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go +++ b/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go @@ -317,20 +317,8 @@ type HistogramMetric struct { // AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet type AutoscalingRunnerSetStatus struct { - // +optional - CurrentRunners int `json:"currentRunners"` - // +optional Phase AutoscalingRunnerSetPhase `json:"phase"` - - // EphemeralRunner counts separated by the stage ephemeral runners are in, taken from the EphemeralRunnerSet - - // +optional - PendingEphemeralRunners int `json:"pendingEphemeralRunners"` - // +optional - RunningEphemeralRunners int `json:"runningEphemeralRunners"` - // +optional - FailedEphemeralRunners int `json:"failedEphemeralRunners"` } type AutoscalingRunnerSetPhase string diff --git a/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go b/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go index 49e21527..3a0ae885 100644 --- a/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go +++ b/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go @@ -40,15 +40,6 @@ 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. - // +optional - CurrentReplicas int `json:"currentReplicas"` - // +optional - PendingEphemeralRunners int `json:"pendingEphemeralRunners"` - // +optional - RunningEphemeralRunners int `json:"runningEphemeralRunners"` - // +optional - FailedEphemeralRunners int `json:"failedEphemeralRunners"` // +optional Phase EphemeralRunnerSetPhase `json:"phase"` } @@ -66,11 +57,6 @@ const ( // +kubebuilder:object:root=true // +kubebuilder:subresource:status // +kubebuilder:printcolumn:JSONPath=".spec.replicas",name="DesiredReplicas",type="integer" -// +kubebuilder:printcolumn:JSONPath=".status.currentReplicas", name="CurrentReplicas",type="integer" -// +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.phase",name=Phase,type=string // EphemeralRunnerSet is the Schema for the ephemeralrunnersets API 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 32c07085..a2dfd46d 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,16 +16541,8 @@ spec: status: description: AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet properties: - currentRunners: - type: integer - failedEphemeralRunners: - type: integer - pendingEphemeralRunners: - type: integer phase: type: string - runningEphemeralRunners: - type: integer type: object type: object served: true 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 eda5dce6..a6f9758b 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 @@ -18,21 +18,6 @@ spec: - jsonPath: .spec.replicas name: DesiredReplicas type: integer - - jsonPath: .status.currentReplicas - name: CurrentReplicas - type: integer - - jsonPath: .status.pendingEphemeralRunners - name: Pending Runners - type: integer - - jsonPath: .status.runningEphemeralRunners - name: Running Runners - type: integer - - jsonPath: .status.finishedEphemeralRunners - name: Finished Runners - type: integer - - jsonPath: .status.deletingEphemeralRunners - name: Deleting Runners - type: integer - jsonPath: .status.phase name: Phase type: string @@ -8310,18 +8295,9 @@ spec: status: description: EphemeralRunnerSetStatus defines the observed state of EphemeralRunnerSet properties: - currentReplicas: - description: CurrentReplicas is the number of currently running EphemeralRunner resources being managed by this EphemeralRunnerSet. - type: integer - failedEphemeralRunners: - type: integer - pendingEphemeralRunners: - type: integer phase: description: EphemeralRunnerSetPhase is the phase of the ephemeral runner set resource type: string - runningEphemeralRunners: - type: integer type: object type: object served: true 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 32c07085..a2dfd46d 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,16 +16541,8 @@ spec: status: description: AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet properties: - currentRunners: - type: integer - failedEphemeralRunners: - type: integer - pendingEphemeralRunners: - type: integer phase: type: string - runningEphemeralRunners: - type: integer type: object type: object served: true 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 eda5dce6..a6f9758b 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 @@ -18,21 +18,6 @@ spec: - jsonPath: .spec.replicas name: DesiredReplicas type: integer - - jsonPath: .status.currentReplicas - name: CurrentReplicas - type: integer - - jsonPath: .status.pendingEphemeralRunners - name: Pending Runners - type: integer - - jsonPath: .status.runningEphemeralRunners - name: Running Runners - type: integer - - jsonPath: .status.finishedEphemeralRunners - name: Finished Runners - type: integer - - jsonPath: .status.deletingEphemeralRunners - name: Deleting Runners - type: integer - jsonPath: .status.phase name: Phase type: string @@ -8310,18 +8295,9 @@ spec: status: description: EphemeralRunnerSetStatus defines the observed state of EphemeralRunnerSet properties: - currentReplicas: - description: CurrentReplicas is the number of currently running EphemeralRunner resources being managed by this EphemeralRunnerSet. - type: integer - failedEphemeralRunners: - type: integer - pendingEphemeralRunners: - type: integer phase: description: EphemeralRunnerSetPhase is the phase of the ephemeral runner set resource type: string - runningEphemeralRunners: - type: integer type: object type: object served: true diff --git a/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml b/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml index 32c07085..a2dfd46d 100644 --- a/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml +++ b/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml @@ -16541,16 +16541,8 @@ spec: status: description: AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet properties: - currentRunners: - type: integer - failedEphemeralRunners: - type: integer - pendingEphemeralRunners: - type: integer phase: type: string - runningEphemeralRunners: - type: integer type: object type: object served: true diff --git a/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml b/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml index eda5dce6..a6f9758b 100644 --- a/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml +++ b/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml @@ -18,21 +18,6 @@ spec: - jsonPath: .spec.replicas name: DesiredReplicas type: integer - - jsonPath: .status.currentReplicas - name: CurrentReplicas - type: integer - - jsonPath: .status.pendingEphemeralRunners - name: Pending Runners - type: integer - - jsonPath: .status.runningEphemeralRunners - name: Running Runners - type: integer - - jsonPath: .status.finishedEphemeralRunners - name: Finished Runners - type: integer - - jsonPath: .status.deletingEphemeralRunners - name: Deleting Runners - type: integer - jsonPath: .status.phase name: Phase type: string @@ -8310,18 +8295,9 @@ spec: status: description: EphemeralRunnerSetStatus defines the observed state of EphemeralRunnerSet properties: - currentReplicas: - description: CurrentReplicas is the number of currently running EphemeralRunner resources being managed by this EphemeralRunnerSet. - type: integer - failedEphemeralRunners: - type: integer - pendingEphemeralRunners: - type: integer phase: description: EphemeralRunnerSetPhase is the phase of the ephemeral runner set resource type: string - runningEphemeralRunners: - type: integer type: object type: object served: true diff --git a/controllers/actions.github.com/autoscalingrunnerset_controller.go b/controllers/actions.github.com/autoscalingrunnerset_controller.go index a07cb350..e48f75ff 100644 --- a/controllers/actions.github.com/autoscalingrunnerset_controller.go +++ b/controllers/actions.github.com/autoscalingrunnerset_controller.go @@ -292,18 +292,6 @@ func (r *AutoscalingRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl } 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 - } - original := ephemeralRunnerSet.DeepCopy() ephemeralRunnerSet.Spec.EphemeralRunnerMetadata = desired.Spec.EphemeralRunnerMetadata ephemeralRunnerSet.Spec.EphemeralRunnerSpec = desired.Spec.EphemeralRunnerSpec @@ -386,7 +374,6 @@ func (r *AutoscalingRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl if err := r.updateStatus( ctx, &autoscalingRunnerSet, - &ephemeralRunnerSet, v1alpha1.AutoscalingRunnerSetPhaseRunning, log, ); err != nil { @@ -432,24 +419,14 @@ func (r *AutoscalingRunnerSetReconciler) cleanUpResources(ctx context.Context, a } // Update the status of autoscaling runner set if necessary -func (r *AutoscalingRunnerSetReconciler) updateStatus(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, ephemeralRunnerSet *v1alpha1.EphemeralRunnerSet, phase v1alpha1.AutoscalingRunnerSetPhase, log logr.Logger) error { - countDiff := ephemeralRunnerSet != nil && ephemeralRunnerSet.Status.CurrentReplicas != autoscalingRunnerSet.Status.CurrentRunners +func (r *AutoscalingRunnerSetReconciler) updateStatus(ctx context.Context, autoscalingRunnerSet *v1alpha1.AutoscalingRunnerSet, phase v1alpha1.AutoscalingRunnerSetPhase, log logr.Logger) error { phaseDiff := phase != autoscalingRunnerSet.Status.Phase - if !countDiff && !phaseDiff { + if !phaseDiff { return nil } original := autoscalingRunnerSet.DeepCopy() - if phaseDiff { - autoscalingRunnerSet.Status.Phase = phase - } - - if countDiff && ephemeralRunnerSet != nil { - autoscalingRunnerSet.Status.CurrentRunners = ephemeralRunnerSet.Status.CurrentReplicas - autoscalingRunnerSet.Status.PendingEphemeralRunners = ephemeralRunnerSet.Status.PendingEphemeralRunners - autoscalingRunnerSet.Status.RunningEphemeralRunners = ephemeralRunnerSet.Status.RunningEphemeralRunners - autoscalingRunnerSet.Status.FailedEphemeralRunners = ephemeralRunnerSet.Status.FailedEphemeralRunners - } + autoscalingRunnerSet.Status.Phase = phase if err := r.Status().Patch(ctx, autoscalingRunnerSet, client.MergeFrom(original)); err != nil { log.Error(err, "Failed to patch autoscaling runner set status") diff --git a/controllers/actions.github.com/autoscalingrunnerset_controller_test.go b/controllers/actions.github.com/autoscalingrunnerset_controller_test.go index 55ebe5c6..11a03dc4 100644 --- a/controllers/actions.github.com/autoscalingrunnerset_controller_test.go +++ b/controllers/actions.github.com/autoscalingrunnerset_controller_test.go @@ -837,111 +837,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( @@ -978,17 +873,10 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { runnerSet := runnerSetList.Items[0] statusUpdate := runnerSet.DeepCopy() - statusUpdate.Status.CurrentReplicas = 6 - statusUpdate.Status.FailedEphemeralRunners = 1 - statusUpdate.Status.RunningEphemeralRunners = 2 - statusUpdate.Status.PendingEphemeralRunners = 3 + statusUpdate.Status.Phase = v1alpha1.EphemeralRunnerSetPhaseRunning desiredStatus := v1alpha1.AutoscalingRunnerSetStatus{ - CurrentRunners: statusUpdate.Status.CurrentReplicas, - Phase: v1alpha1.AutoscalingRunnerSetPhaseRunning, - PendingEphemeralRunners: statusUpdate.Status.PendingEphemeralRunners, - RunningEphemeralRunners: statusUpdate.Status.RunningEphemeralRunners, - FailedEphemeralRunners: statusUpdate.Status.FailedEphemeralRunners, + Phase: v1alpha1.AutoscalingRunnerSetPhaseRunning, } err := k8sClient.Status().Patch(ctx, statusUpdate, client.MergeFrom(&runnerSet)) diff --git a/controllers/actions.github.com/ephemeralrunner_controller.go b/controllers/actions.github.com/ephemeralrunner_controller.go index 64b98e27..e78ede68 100644 --- a/controllers/actions.github.com/ephemeralrunner_controller.go +++ b/controllers/actions.github.com/ephemeralrunner_controller.go @@ -22,9 +22,12 @@ import ( "fmt" "strconv" "strings" + "sync" "time" "github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1" + "github.com/actions/actions-runner-controller/controllers/actions.github.com/metrics" + "github.com/actions/actions-runner-controller/github/actions" "github.com/actions/scaleset" "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" @@ -46,11 +49,19 @@ const ( // EphemeralRunnerReconciler reconciles a EphemeralRunner object type EphemeralRunnerReconciler struct { client.Client - Log logr.Logger - Scheme *runtime.Scheme + Log logr.Logger + Scheme *runtime.Scheme + PublishMetrics bool ResourceBuilder } +var ephemeralRunnerPhaseMetrics = struct { + sync.Mutex + phases map[types.NamespacedName]v1alpha1.EphemeralRunnerPhase +}{ + phases: map[types.NamespacedName]v1alpha1.EphemeralRunnerPhase{}, +} + // precompute backoff durations for failed ephemeral runners // the len(failedRunnerBackoff) must be equal to maxFailures + 1 var failedRunnerBackoff = []time.Duration{ @@ -86,6 +97,8 @@ func (r *EphemeralRunnerReconciler) Reconcile(ctx context.Context, req ctrl.Requ original := ephemeralRunner.DeepCopy() if !ephemeralRunner.DeletionTimestamp.IsZero() { + r.publishEphemeralRunnerPhaseMetric(&ephemeralRunner, "", log) + if !controllerutil.ContainsFinalizer(&ephemeralRunner, ephemeralRunnerFinalizerName) { return ctrl.Result{}, nil } @@ -142,6 +155,8 @@ func (r *EphemeralRunnerReconciler) Reconcile(ctx context.Context, req ctrl.Requ return ctrl.Result{}, nil } + r.publishEphemeralRunnerPhaseMetric(&ephemeralRunner, ephemeralRunner.Status.Phase, log) + if ephemeralRunner.IsDone() { log.Info("Cleaning up resources after after ephemeral runner termination", "phase", ephemeralRunner.Status.Phase) err := r.cleanupResources(ctx, &ephemeralRunner, log) @@ -337,6 +352,10 @@ func (r *EphemeralRunnerReconciler) Reconcile(ctx context.Context, req ctrl.Requ // If the runner container exits with 0, we assume that the runner has finished successfully. // If side-car container exits with non-zero, it shouldn't affect the runner. Runner exit code // drives the controller's inference of whether the job has succeeded or failed. + if err := r.markAsSucceeded(ctx, &ephemeralRunner, pod, log); err != nil { + log.Error(err, "Failed to set ephemeral runner to phase Succeeded") + return ctrl.Result{}, err + } if err := r.Delete(ctx, &ephemeralRunner); err != nil { log.Error(err, "Failed to delete ephemeral runner after successful completion") return ctrl.Result{}, err @@ -390,6 +409,10 @@ func (r *EphemeralRunnerReconciler) Reconcile(ctx context.Context, req ctrl.Requ default: // succeeded log.Info("Ephemeral runner has finished successfully, deleting ephemeral runner", "exitCode", cs.State.Terminated.ExitCode) + if err := r.markAsSucceeded(ctx, &ephemeralRunner, pod, log); err != nil { + log.Error(err, "Failed to set ephemeral runner to phase Succeeded") + return ctrl.Result{}, err + } if err := r.Delete(ctx, &ephemeralRunner); err != nil { log.Error(err, "Failed to delete ephemeral runner after successful completion") return ctrl.Result{}, err @@ -583,6 +606,7 @@ func (r *EphemeralRunnerReconciler) markAsFailed(ctx context.Context, ephemeralR if err := r.Status().Patch(ctx, ephemeralRunner, client.MergeFrom(original)); err != nil { return fmt.Errorf("failed to update ephemeral runner status Phase/Message: %w", err) } + r.publishEphemeralRunnerPhaseMetric(ephemeralRunner, ephemeralRunner.Status.Phase, log) log.Info("Removing the runner from the service") if err := r.deleteRunnerFromService(ctx, ephemeralRunner, log); err != nil { @@ -604,6 +628,7 @@ func (r *EphemeralRunnerReconciler) markAsOutdated(ctx context.Context, ephemera if err := r.Status().Patch(ctx, ephemeralRunner, client.MergeFrom(original)); err != nil { return fmt.Errorf("failed to update ephemeral runner status Phase/Message: %w", err) } + r.publishEphemeralRunnerPhaseMetric(ephemeralRunner, ephemeralRunner.Status.Phase, log) log.Info("Removing the runner from the service") if err := r.deleteRunnerFromService(ctx, ephemeralRunner, log); err != nil { @@ -612,6 +637,23 @@ func (r *EphemeralRunnerReconciler) markAsOutdated(ctx context.Context, ephemera return nil } +func (r *EphemeralRunnerReconciler) markAsSucceeded(ctx context.Context, ephemeralRunner *v1alpha1.EphemeralRunner, pod *corev1.Pod, log logr.Logger) error { + log.Info("Updating ephemeral runner status to Succeeded") + + original := ephemeralRunner.DeepCopy() + ephemeralRunner.Status.Phase = v1alpha1.EphemeralRunnerPhaseSucceeded + ephemeralRunner.Status.Ready = false + ephemeralRunner.Status.Reason = pod.Status.Reason + ephemeralRunner.Status.Message = pod.Status.Message + if err := r.Status().Patch(ctx, ephemeralRunner, client.MergeFrom(original)); err != nil { + return fmt.Errorf("failed to update ephemeral runner status Phase/Message: %w", err) + } + r.publishEphemeralRunnerPhaseMetric(ephemeralRunner, ephemeralRunner.Status.Phase, log) + + log.Info("EphemeralRunner is marked as Succeeded") + return nil +} + // deletePodAsFailed is responsible for deleting the pod and updating the .Status.Failures for tracking failure count. // It should not be responsible for setting the status to Failed. // @@ -835,11 +877,61 @@ func (r *EphemeralRunnerReconciler) updateRunStatusFromPod(ctx context.Context, if err := r.Status().Patch(ctx, ephemeralRunner, client.MergeFrom(original)); err != nil { return fmt.Errorf("failed to update runner status for Phase/Reason/Message/Ready: %w", err) } + r.publishEphemeralRunnerPhaseMetric(ephemeralRunner, ephemeralRunner.Status.Phase, log) log.Info("Updated ephemeral runner status") return nil } +func (r *EphemeralRunnerReconciler) publishEphemeralRunnerPhaseMetric(ephemeralRunner *v1alpha1.EphemeralRunner, phase v1alpha1.EphemeralRunnerPhase, log logr.Logger) { + if !r.PublishMetrics { + return + } + + commonLabels, err := ephemeralRunnerMetricLabels(ephemeralRunner) + if err != nil { + log.Error(err, "Failed to build ephemeral runner metric labels") + return + } + + key := types.NamespacedName{Namespace: ephemeralRunner.Namespace, Name: ephemeralRunner.Name} + + ephemeralRunnerPhaseMetrics.Lock() + defer ephemeralRunnerPhaseMetrics.Unlock() + + previousPhase, ok := ephemeralRunnerPhaseMetrics.phases[key] + if ok && previousPhase == phase { + return + } + + if ok { + metrics.SubEphemeralRunner(commonLabels, previousPhase) + } + + if phase == "" { + delete(ephemeralRunnerPhaseMetrics.phases, key) + return + } + + metrics.AddEphemeralRunner(commonLabels, phase) + ephemeralRunnerPhaseMetrics.phases[key] = phase +} + +func ephemeralRunnerMetricLabels(ephemeralRunner *v1alpha1.EphemeralRunner) (metrics.CommonLabels, error) { + parsedURL, err := actions.ParseGitHubConfigFromURL(ephemeralRunner.Spec.GitHubConfigURL) + if err != nil { + return metrics.CommonLabels{}, fmt.Errorf("github config URL is invalid: %w", err) + } + + return metrics.CommonLabels{ + Name: ephemeralRunner.Labels[LabelKeyGitHubScaleSetName], + Namespace: ephemeralRunner.Labels[LabelKeyGitHubScaleSetNamespace], + Repository: parsedURL.Repository, + Organization: parsedURL.Organization, + Enterprise: parsedURL.Enterprise, + }, nil +} + func (r *EphemeralRunnerReconciler) deleteRunnerFromService(ctx context.Context, ephemeralRunner *v1alpha1.EphemeralRunner, log logr.Logger) error { client, err := r.GetActionsService(ctx, ephemeralRunner) if err != nil { diff --git a/controllers/actions.github.com/ephemeralrunnerset_controller.go b/controllers/actions.github.com/ephemeralrunnerset_controller.go index 92faae85..919a4641 100644 --- a/controllers/actions.github.com/ephemeralrunnerset_controller.go +++ b/controllers/actions.github.com/ephemeralrunnerset_controller.go @@ -27,9 +27,7 @@ import ( "time" "github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1" - "github.com/actions/actions-runner-controller/controllers/actions.github.com/metrics" "github.com/actions/actions-runner-controller/controllers/actions.github.com/multiclient" - "github.com/actions/actions-runner-controller/github/actions" "github.com/actions/scaleset" "github.com/go-logr/logr" "go.uber.org/multierr" @@ -51,9 +49,8 @@ const ( // EphemeralRunnerSetReconciler reconciles a EphemeralRunnerSet object type EphemeralRunnerSetReconciler struct { client.Client - Log logr.Logger - Scheme *runtime.Scheme - PublishMetrics bool + Log logr.Logger + Scheme *runtime.Scheme ResourceBuilder } @@ -205,29 +202,6 @@ func (r *EphemeralRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl.R "deleting", len(ephemeralRunnersByState.deleting), ) - if r.PublishMetrics { - githubConfigURL := ephemeralRunnerSet.Spec.EphemeralRunnerSpec.GitHubConfigURL - parsedURL, err := actions.ParseGitHubConfigFromURL(githubConfigURL) - if err != nil { - log.Error(err, "Github Config URL is invalid", "URL", githubConfigURL) - // stop reconciling on this object - return ctrl.Result{}, nil - } - - metrics.SetEphemeralRunnerCountsByStatus( - metrics.CommonLabels{ - Name: ephemeralRunnerSet.Labels[LabelKeyGitHubScaleSetName], - Namespace: ephemeralRunnerSet.Labels[LabelKeyGitHubScaleSetNamespace], - Repository: parsedURL.Repository, - Organization: parsedURL.Organization, - Enterprise: parsedURL.Enterprise, - }, - len(ephemeralRunnersByState.pending), - len(ephemeralRunnersByState.running), - len(ephemeralRunnersByState.failed), - ) - } - total := ephemeralRunnersByState.scaleTotal() if ephemeralRunnerSet.Spec.PatchID == 0 || ephemeralRunnerSet.Spec.PatchID != ephemeralRunnersByState.latestPatchID { defer func() { @@ -272,7 +246,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 { case len(state.outdated) > 0: @@ -283,11 +256,7 @@ func (r *EphemeralRunnerSetReconciler) updateStatus(ctx context.Context, ephemer phase = ephemeralRunnerSet.Status.Phase } desiredStatus := v1alpha1.EphemeralRunnerSetStatus{ - CurrentReplicas: total, - Phase: phase, - PendingEphemeralRunners: len(state.pending), - RunningEphemeralRunners: len(state.running), - FailedEphemeralRunners: len(state.failed), + Phase: phase, } // Update the status if needed. diff --git a/controllers/actions.github.com/ephemeralrunnerset_controller_test.go b/controllers/actions.github.com/ephemeralrunnerset_controller_test.go index c3459652..76cf98ab 100644 --- a/controllers/actions.github.com/ephemeralrunnerset_controller_test.go +++ b/controllers/actions.github.com/ephemeralrunnerset_controller_test.go @@ -9,14 +9,21 @@ import ( "os" "path/filepath" "strings" + "sync" "testing" "time" + "github.com/actions/actions-runner-controller/controllers/actions.github.com/metrics" + "github.com/actions/actions-runner-controller/github/actions" + "github.com/actions/scaleset" + prometheusdto "github.com/prometheus/client_model/go" corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" + controllerMetrics "sigs.k8s.io/controller-runtime/pkg/metrics" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -34,10 +41,92 @@ const ( ephemeralRunnerSetTestInterval = time.Millisecond * 250 ) +var registerActionsGithubMetricsForTest sync.Once + func TestPrecomputedConstants(t *testing.T) { require.Equal(t, len(failedRunnerBackoff), maxFailures+1) } +func expectEphemeralRunnerPhase(ctx context.Context, ephemeralRunner *v1alpha1.EphemeralRunner, phase v1alpha1.EphemeralRunnerPhase) { + updated := new(v1alpha1.EphemeralRunner) + err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, updated) + Expect(err).NotTo(HaveOccurred(), "failed to get ephemeral runner") + Expect(updated.Status.Phase).To(Equal(phase)) +} + +func expectEphemeralRunnerPhaseMetric(ephemeralRunner *v1alpha1.EphemeralRunner, phase v1alpha1.EphemeralRunnerPhase, expected float64) { + metricName := ephemeralRunnerPhaseMetricName(phase) + Expect(metricName).NotTo(BeEmpty(), "unexpected ephemeral runner phase") + + parsedURL, err := actions.ParseGitHubConfigFromURL(ephemeralRunner.Spec.GitHubConfigURL) + Expect(err).NotTo(HaveOccurred(), "failed to parse GitHub config URL") + + value, err := ephemeralRunnerPhaseMetricValue(metricName, map[string]string{ + "name": ephemeralRunner.Labels[LabelKeyGitHubScaleSetName], + "namespace": ephemeralRunner.Labels[LabelKeyGitHubScaleSetNamespace], + "repository": parsedURL.Repository, + "organization": parsedURL.Organization, + "enterprise": parsedURL.Enterprise, + }) + Expect(err).NotTo(HaveOccurred(), "failed to gather ephemeral runner phase metrics") + Expect(value).To(Equal(expected)) +} + +func ephemeralRunnerPhaseMetricName(phase v1alpha1.EphemeralRunnerPhase) string { + switch phase { + case v1alpha1.EphemeralRunnerPhasePending: + return "gha_controller_pending_ephemeral_runners" + case v1alpha1.EphemeralRunnerPhaseRunning: + return "gha_controller_running_ephemeral_runners" + case v1alpha1.EphemeralRunnerPhaseSucceeded: + return "gha_controller_succeeded_ephemeral_runners" + case v1alpha1.EphemeralRunnerPhaseFailed: + return "gha_controller_failed_ephemeral_runners" + case v1alpha1.EphemeralRunnerPhaseOutdated: + return "gha_controller_outdated_ephemeral_runners" + default: + return "" + } +} + +func ephemeralRunnerPhaseMetricValue(metricName string, labels map[string]string) (float64, error) { + metricFamilies, err := controllerMetrics.Registry.Gather() + if err != nil { + return 0, err + } + + for _, metricFamily := range metricFamilies { + if metricFamily.GetName() != metricName { + continue + } + + for _, metric := range metricFamily.GetMetric() { + if metricHasLabels(metric, labels) { + if metric.GetGauge() == nil { + return 0, nil + } + return metric.GetGauge().GetValue(), nil + } + } + } + + return 0, nil +} + +func metricHasLabels(metric *prometheusdto.Metric, labels map[string]string) bool { + metricLabels := map[string]string{} + for _, label := range metric.GetLabel() { + metricLabels[label.GetName()] = label.GetValue() + } + + for name, value := range labels { + if metricLabels[name] != value { + return false + } + } + return true +} + var _ = Describe("Test EphemeralRunnerSet controller", func() { var ctx context.Context var mgr ctrl.Manager @@ -133,20 +222,20 @@ var _ = Describe("Test EphemeralRunnerSet controller", func() { ephemeralRunnerSetTestInterval, ).Should(BeEquivalentTo(0), "No EphemeralRunner should be created") - // Check if the status stay 0 + // Check if the status is initialized Consistently( - func() (int, error) { + func() (v1alpha1.EphemeralRunnerSetPhase, error) { runnerSet := new(v1alpha1.EphemeralRunnerSet) err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunnerSet.Name, Namespace: ephemeralRunnerSet.Namespace}, runnerSet) if err != nil { - return -1, err + return "", err } - return int(runnerSet.Status.CurrentReplicas), nil + return runnerSet.Status.Phase, nil }, ephemeralRunnerSetTestTimeout, ephemeralRunnerSetTestInterval, - ).Should(BeEquivalentTo(0), "EphemeralRunnerSet status should be 0") + ).Should(BeEquivalentTo(v1alpha1.EphemeralRunnerSetPhaseRunning), "EphemeralRunnerSet status should be running") // Scaling up the EphemeralRunnerSet updated := created.DeepCopy() @@ -187,20 +276,20 @@ var _ = Describe("Test EphemeralRunnerSet controller", func() { ephemeralRunnerSetTestInterval, ).Should(BeEquivalentTo(5), "5 EphemeralRunner should be created") - // Check if the status is updated + // Check if the status stays running Eventually( - func() (int, error) { + func() (v1alpha1.EphemeralRunnerSetPhase, error) { runnerSet := new(v1alpha1.EphemeralRunnerSet) err := k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunnerSet.Name, Namespace: ephemeralRunnerSet.Namespace}, runnerSet) if err != nil { - return -1, err + return "", err } - return int(runnerSet.Status.CurrentReplicas), nil + return runnerSet.Status.Phase, nil }, ephemeralRunnerSetTestTimeout, ephemeralRunnerSetTestInterval, - ).Should(BeEquivalentTo(5), "EphemeralRunnerSet status should be 5") + ).Should(BeEquivalentTo(v1alpha1.EphemeralRunnerSetPhaseRunning), "EphemeralRunnerSet status should be running") }) }) @@ -1184,11 +1273,7 @@ var _ = Describe("Test EphemeralRunnerSet controller", func() { ).Should(BeTrue(), "Failed to eventually update to one pending, one running and one failed") desiredStatus := v1alpha1.EphemeralRunnerSetStatus{ - Phase: v1alpha1.EphemeralRunnerSetPhaseRunning, - CurrentReplicas: 3, - PendingEphemeralRunners: 1, - RunningEphemeralRunners: 1, - FailedEphemeralRunners: 1, + Phase: v1alpha1.EphemeralRunnerSetPhaseRunning, } Eventually( func() (v1alpha1.EphemeralRunnerSetStatus, error) { @@ -1227,11 +1312,7 @@ var _ = Describe("Test EphemeralRunnerSet controller", func() { ).Should(BeEquivalentTo(1), "Failed to eventually scale down") desiredStatus = v1alpha1.EphemeralRunnerSetStatus{ - CurrentReplicas: 1, - PendingEphemeralRunners: 0, - RunningEphemeralRunners: 0, - FailedEphemeralRunners: 1, - Phase: v1alpha1.EphemeralRunnerSetPhaseRunning, + Phase: v1alpha1.EphemeralRunnerSetPhaseRunning, } Eventually( @@ -1251,11 +1332,7 @@ var _ = Describe("Test EphemeralRunnerSet controller", func() { Expect(err).To(BeNil(), "Failed to delete failed ephemeral runner") desiredStatus = v1alpha1.EphemeralRunnerSetStatus{ - CurrentReplicas: 0, - PendingEphemeralRunners: 0, - RunningEphemeralRunners: 0, - FailedEphemeralRunners: 0, - Phase: v1alpha1.EphemeralRunnerSetPhaseRunning, + Phase: v1alpha1.EphemeralRunnerSetPhaseRunning, } Eventually( func() (v1alpha1.EphemeralRunnerSetStatus, error) { @@ -1273,6 +1350,148 @@ var _ = Describe("Test EphemeralRunnerSet controller", func() { }) }) +var _ = Describe("EphemeralRunner phase metrics", func() { + var ctx context.Context + var autoscalingNS *corev1.Namespace + var mgr ctrl.Manager + var configSecret *corev1.Secret + var controller *EphemeralRunnerReconciler + var ephemeralRunner *v1alpha1.EphemeralRunner + var request ctrl.Request + + BeforeEach(func() { + registerActionsGithubMetricsForTest.Do(func() { + metrics.RegisterMetrics() + }) + + ephemeralRunnerPhaseMetrics.Lock() + ephemeralRunnerPhaseMetrics.phases = map[types.NamespacedName]v1alpha1.EphemeralRunnerPhase{} + ephemeralRunnerPhaseMetrics.Unlock() + + ctx = context.Background() + autoscalingNS, mgr = createNamespace(GinkgoT(), k8sClient) + configSecret = createDefaultSecret(GinkgoT(), k8sClient, autoscalingNS.Name) + + controller = &EphemeralRunnerReconciler{ + Client: k8sClient, + Scheme: mgr.GetScheme(), + Log: logf.Log, + PublishMetrics: true, + ResourceBuilder: ResourceBuilder{ + SecretResolver: secretresolver.New(k8sClient, fake.NewMultiClient( + fake.WithClient( + fake.NewClient( + fake.WithGenerateJitRunnerConfig( + &scaleset.RunnerScaleSetJitRunnerConfig{ + Runner: &scaleset.RunnerReference{ID: 100, Name: "test-runner"}, + EncodedJITConfig: "fake-jit-config", + }, + nil, + ), + ), + ), + )), + }, + } + + ephemeralRunner = &v1alpha1.EphemeralRunner{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-runner", + Namespace: autoscalingNS.Name, + Labels: map[string]string{ + LabelKeyGitHubScaleSetName: "test-scale-set", + LabelKeyGitHubScaleSetNamespace: autoscalingNS.Name, + }, + }, + Spec: v1alpha1.EphemeralRunnerSpec{ + GitHubConfigURL: "https://github.com/owner/repo", + GitHubConfigSecret: configSecret.Name, + RunnerScaleSetID: 100, + PodTemplateSpec: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: v1alpha1.EphemeralRunnerContainerName, + Image: "ghcr.io/actions/runner", + }, + }, + }, + }, + }, + } + + err := k8sClient.Create(ctx, ephemeralRunner) + Expect(err).NotTo(HaveOccurred(), "failed to create ephemeral runner") + + request = ctrl.Request{NamespacedName: types.NamespacedName{Namespace: ephemeralRunner.Namespace, Name: ephemeralRunner.Name}} + }) + + It("publishes pending, running, and succeeded phase transitions", func() { + _, err := controller.Reconcile(ctx, request) + Expect(err).NotTo(HaveOccurred(), "failed to reconcile ephemeral runner") + + pod := new(corev1.Pod) + Eventually(func() error { + return k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunner.Name, Namespace: ephemeralRunner.Namespace}, pod) + }, ephemeralRunnerSetTestTimeout, ephemeralRunnerSetTestInterval).Should(Succeed(), "expected ephemeral runner pod to be created") + + podPending := pod.DeepCopy() + podPending.Status.Phase = corev1.PodPending + podPending.Status.ContainerStatuses = []corev1.ContainerStatus{ + { + Name: v1alpha1.EphemeralRunnerContainerName, + State: corev1.ContainerState{}, + }, + } + err = k8sClient.Status().Patch(ctx, podPending, client.MergeFrom(pod)) + Expect(err).NotTo(HaveOccurred(), "failed to patch pod to pending") + + _, err = controller.Reconcile(ctx, request) + Expect(err).NotTo(HaveOccurred(), "failed to reconcile pending pod") + expectEphemeralRunnerPhase(ctx, ephemeralRunner, v1alpha1.EphemeralRunnerPhasePending) + expectEphemeralRunnerPhaseMetric(ephemeralRunner, v1alpha1.EphemeralRunnerPhasePending, 1) + expectEphemeralRunnerPhaseMetric(ephemeralRunner, v1alpha1.EphemeralRunnerPhaseRunning, 0) + + podRunning := podPending.DeepCopy() + podRunning.Status.Phase = corev1.PodRunning + podRunning.Status.ContainerStatuses = []corev1.ContainerStatus{ + { + Name: v1alpha1.EphemeralRunnerContainerName, + State: corev1.ContainerState{ + Running: &corev1.ContainerStateRunning{StartedAt: metav1.Now()}, + }, + }, + } + err = k8sClient.Status().Patch(ctx, podRunning, client.MergeFrom(podPending)) + Expect(err).NotTo(HaveOccurred(), "failed to patch pod to running") + + _, err = controller.Reconcile(ctx, request) + Expect(err).NotTo(HaveOccurred(), "failed to reconcile running pod") + expectEphemeralRunnerPhase(ctx, ephemeralRunner, v1alpha1.EphemeralRunnerPhaseRunning) + expectEphemeralRunnerPhaseMetric(ephemeralRunner, v1alpha1.EphemeralRunnerPhasePending, 0) + expectEphemeralRunnerPhaseMetric(ephemeralRunner, v1alpha1.EphemeralRunnerPhaseRunning, 1) + + podSucceeded := podRunning.DeepCopy() + podSucceeded.Status.Phase = corev1.PodSucceeded + podSucceeded.Status.ContainerStatuses = []corev1.ContainerStatus{ + { + Name: v1alpha1.EphemeralRunnerContainerName, + State: corev1.ContainerState{ + Terminated: &corev1.ContainerStateTerminated{ExitCode: 0}, + }, + }, + } + err = k8sClient.Status().Patch(ctx, podSucceeded, client.MergeFrom(podRunning)) + Expect(err).NotTo(HaveOccurred(), "failed to patch pod to succeeded") + + _, err = controller.Reconcile(ctx, request) + Expect(err).NotTo(HaveOccurred(), "failed to reconcile succeeded pod") + expectEphemeralRunnerPhase(ctx, ephemeralRunner, v1alpha1.EphemeralRunnerPhaseSucceeded) + expectEphemeralRunnerPhaseMetric(ephemeralRunner, v1alpha1.EphemeralRunnerPhaseRunning, 0) + expectEphemeralRunnerPhaseMetric(ephemeralRunner, v1alpha1.EphemeralRunnerPhaseSucceeded, 1) + }) +}) + var _ = Describe("Test EphemeralRunnerSet controller with proxy settings", func() { var ctx context.Context var mgr ctrl.Manager diff --git a/controllers/actions.github.com/metrics/metrics.go b/controllers/actions.github.com/metrics/metrics.go index 8e137514..1d1f2867 100644 --- a/controllers/actions.github.com/metrics/metrics.go +++ b/controllers/actions.github.com/metrics/metrics.go @@ -1,6 +1,7 @@ package metrics import ( + "github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1" "github.com/prometheus/client_golang/prometheus" "sigs.k8s.io/controller-runtime/pkg/metrics" ) @@ -50,6 +51,14 @@ var ( }, labels, ) + succeededEphemeralRunners = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Subsystem: githubScaleSetControllerSubsystem, + Name: "succeeded_ephemeral_runners", + Help: "Number of ephemeral runners in a succeeded state.", + }, + labels, + ) failedEphemeralRunners = prometheus.NewGaugeVec( prometheus.GaugeOpts{ Subsystem: githubScaleSetControllerSubsystem, @@ -58,6 +67,14 @@ var ( }, labels, ) + outdatedEphemeralRunners = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Subsystem: githubScaleSetControllerSubsystem, + Name: "outdated_ephemeral_runners", + Help: "Number of ephemeral runners in an outdated state.", + }, + labels, + ) runningListeners = prometheus.NewGaugeVec( prometheus.GaugeOpts{ Subsystem: githubScaleSetControllerSubsystem, @@ -72,15 +89,34 @@ func RegisterMetrics() { metrics.Registry.MustRegister( pendingEphemeralRunners, runningEphemeralRunners, + succeededEphemeralRunners, failedEphemeralRunners, + outdatedEphemeralRunners, runningListeners, ) } -func SetEphemeralRunnerCountsByStatus(commonLabels CommonLabels, pending, running, failed int) { - pendingEphemeralRunners.With(commonLabels.labels()).Set(float64(pending)) - runningEphemeralRunners.With(commonLabels.labels()).Set(float64(running)) - failedEphemeralRunners.With(commonLabels.labels()).Set(float64(failed)) +func AddEphemeralRunner(commonLabels CommonLabels, phase v1alpha1.EphemeralRunnerPhase) { + updateEphemeralRunner(commonLabels, phase, 1) +} + +func SubEphemeralRunner(commonLabels CommonLabels, phase v1alpha1.EphemeralRunnerPhase) { + updateEphemeralRunner(commonLabels, phase, -1) +} + +func updateEphemeralRunner(commonLabels CommonLabels, phase v1alpha1.EphemeralRunnerPhase, delta float64) { + switch phase { + case v1alpha1.EphemeralRunnerPhasePending: + pendingEphemeralRunners.With(commonLabels.labels()).Add(delta) + case v1alpha1.EphemeralRunnerPhaseRunning: + runningEphemeralRunners.With(commonLabels.labels()).Add(delta) + case v1alpha1.EphemeralRunnerPhaseSucceeded: + succeededEphemeralRunners.With(commonLabels.labels()).Add(delta) + case v1alpha1.EphemeralRunnerPhaseFailed: + failedEphemeralRunners.With(commonLabels.labels()).Add(delta) + case v1alpha1.EphemeralRunnerPhaseOutdated: + outdatedEphemeralRunners.With(commonLabels.labels()).Add(delta) + } } func AddRunningListener(commonLabels CommonLabels) { diff --git a/github/actions/testdata/intermediate.crt b/github/actions/testdata/intermediate.crt index 476da8e0..99826f20 100644 --- a/github/actions/testdata/intermediate.crt +++ b/github/actions/testdata/intermediate.crt @@ -1,19 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIDKjCCAhKgAwIBAgIUQr7R8yN5+2and6ucUOPF6oIbD44wDQYJKoZIhvcNAQEL -BQAwFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTI1MDIyODEyMDEzMFoXDTI3 -MTEyNTEyMDEzMFowHzEdMBsGA1UEAwwUVGVzdCBJbnRlcm1lZGlhdGUgQ0EwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDOGvN95wCkYO35qyJnf/RwTiDb -oEVaefKnZZny1JrO34MFjlAz8C/P5WwxNUzzbQLTPh5iTqFRU+vis6HPvV0HJEoI -wTfgBCZxcdY8fEIY96FGHLju3PzfxeJaVHyi+2cTtzU+oNp4OFF8huApjYXjaV4y -pAirPbiiP/cgtcT4L5WErQi0aGZkq+1YqY2duNFNIGPTEcXV4iN4IhuD9dpqdKFg -H0wmZDgH+VE/5ACXovU8j5cxCKOJGxTVMKVZlvxPH3w69Z85x3o5AAnyxwo8E2zo -TC1FJ1eFLsmYLZki6cGBzSkIl5QlLGHakWYh+JLu/pkfTL8t+AkY3hZJM96ZAgMB +MIIDKjCCAhKgAwIBAgIUVy/UETGamzkzSYS3TortnzNWCekwDQYJKoZIhvcNAQEL +BQAwFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTI2MDcxMzEzMjQwN1oXDTI5 +MDQwODEzMjQwN1owHzEdMBsGA1UEAwwUVGVzdCBJbnRlcm1lZGlhdGUgQ0EwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDzbccB7wpLODrRW3IePtAmKk63 +8pVYb9yZZL5KjLjli+xpWURvS1N8X44NyU67nkArsO3UG3kq8KGYU03Dy5iCQmlT +hXddmpNIAc56CF/vYBh7hzklrF1qFMgwHl48LsemiYfn+vizonzd+UmrKqhghDRy +SwNLnzSCWBgWwaJPyKlp9pUXpxmSjU+1ByRaLl5E2tmCA3ghdJPXayQeZz+nWcxv +PWzo5EyD5SVm45WYxPKuFalAmmwMnSZxcTBvl/SlHhSR5o40bQ/KOqkBHq/rpEAK +ps7W9pnn2xgq3KOTf554ymIqTyfdInlOkh+2b2joVIS2/fVVDuTpeGn4NE7DAgMB AAGjZjBkMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMB0GA1Ud -DgQWBBSmAyntm95+KoyL3ffLBXZKSpk1VTAfBgNVHSMEGDAWgBR93+rQFjh+RUFX -df4dbNcfS2hbTzANBgkqhkiG9w0BAQsFAAOCAQEABopVFLGQf/LFH+OKVCOT8FCC -y/+o1B/U5jXVvbfwlSGScaiJGQ94FsuH59XJCGySQj77ZVTeElBtntoLXmOCFjyF -jKHCDfUpB4nzeqNMvTDzuoYyPS8DhoGfEnaCgJyKf6GU4p41502gH8mQRB7azzL7 -5jW0aFatCA6G6T1oogHZpHf0ice80C2JkFbWHSE9JxqARbTc06wCDBiSBFTGZQDO -JaBIbn6FL3zSkKcpwgJEqDRavVuoDUlJPDqtTzjf/fMQGGR2LUFkceJpsQqf1jrF -1yTtEZ8gjR2g2Vj6IszUAgbc87xR0AgyGDVckiUdhlX2Y6KCqo2cl9LfSVpqtw== +DgQWBBTjYR9enoN6IY3lzzq7hF4R3Pxr7jAfBgNVHSMEGDAWgBS92jtC5lTGLF1Y +ZRVMVU7lo4qtbDANBgkqhkiG9w0BAQsFAAOCAQEAlFifQlOXNpUQxbSXmjZj40dW +kXhm3s7rBfv6jLm7ebGkbKQ9SPqM94hi9nFX2BWD9DKltHJgmv6mvwWYj0/YO5ij +/A7ckNy3Xf7vu1L3h9CUViZM8d9HMCAnneQihF8X0W+yUwHVhZTFDedl4JMWcttL +zHMtwlhbaFZhPolkvkNksZRvAOak2O6LUOURvw/alt3tVI/zOyomH5DYi7uxMXXj +H7/61rLDdE5cLWIgpcCDDKQi1ORP6w5/HaQ5+1G/IA2PMN0jS903hvxJqTki3XLi +uygYXVa4HETKowgXWXLsNHnMHExpE6kPPtU9v/1t3Lah5H/D3qgicZ1DvMNMaA== -----END CERTIFICATE----- diff --git a/github/actions/testdata/leaf.crt b/github/actions/testdata/leaf.crt index 1d05047e..9dcae701 100644 --- a/github/actions/testdata/leaf.crt +++ b/github/actions/testdata/leaf.crt @@ -1,20 +1,20 @@ -----BEGIN CERTIFICATE----- -MIIDQjCCAiqgAwIBAgIUHT3JtqsYKs7NHv1LNyS9RYC7vsAwDQYJKoZIhvcNAQEL -BQAwHzEdMBsGA1UEAwwUVGVzdCBJbnRlcm1lZGlhdGUgQ0EwHhcNMjUwMjI4MTIw -MTMwWhcNMjYwNzEzMTIwMTMwWjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0G -CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHwI/xSLgCuQrx+WsvupA8w4eMdSef -WGw523OJYPJkDYJGgSGsjVb9htba6vXYbGNohuluEAZIyT7GvmPezTokeVMkuSYT -lSV8xplFEtDlQhTzaI/cofbi7qtT91/5zS/w0JSaNosThGtZg/M4ZOiMj04m0NGK -Zz56l9Lpe/yM7fPda++D9xYEGSSdwK9CqqwF+cXN09d6IK1VINIIjT3Sdb9Sssok -GWmD7UUPLvwZ5379+HRs1K8AFXqvbkeWVYtrJwJMxJGVnNSeiqKGSmMEpP7tVNHl -s4V7oyQXd8KX+HpziiayjGy9giVteJJi/bAmUp+0+hTHBes5fOWI5JyDAgMBAAGj -gYAwfjAfBgNVHSMEGDAWgBSmAyntm95+KoyL3ffLBXZKSpk1VTAJBgNVHRMEAjAA +MIIDQjCCAiqgAwIBAgIUbiA5aoS2wqoRlN7qlS1CUrQRrZIwDQYJKoZIhvcNAQEL +BQAwHzEdMBsGA1UEAwwUVGVzdCBJbnRlcm1lZGlhdGUgQ0EwHhcNMjYwNzEzMTMy +NDA3WhcNMjcxMTI1MTMyNDA3WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDf2xz5c/Kts7iYFbznIBqnos0OZw1m +XqQRfd8Ub7mcRcNu5LAS+QXaMCQrtSS8HaiOWrnmXVnY0Dtm9BYYgxCLbffBjPgv +CjMURftuI0fvFfFHhNOeuO1Y6N3z4YFY9UHldiqBIKmzrEKl6ZzibbHhIWlOFdMC +IHosnO+9LgXgEcr242yrBczl2SWycXG4OGag4yp2FrsvrKtdqyErJBuyIQHv51k6 +Eo5vz42UxZCcjBciSe/X2Tf+KDLzslZDfnY+8PWU2Dx5DaMZAxNVa78jRDhKact9 +jOunKd1JRj/GZkV5EKL/lIJJQPgcNCl0s8swKlicEYpqEdS4Ayj0fF7DAgMBAAGj +gYAwfjAfBgNVHSMEGDAWgBTjYR9enoN6IY3lzzq7hF4R3Pxr7jAJBgNVHRMEAjAA MAsGA1UdDwQEAwIFoDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHREECDAGhwR/ -AAABMB0GA1UdDgQWBBTVdJE2lkGsNIU3LlEf3rN5fyaRkjANBgkqhkiG9w0BAQsF -AAOCAQEAo1klH9WMsPWTN9qN3tdud07eatulEKo/0okaph6MJ59ozseOzxrfpwL0 -67Nr8yl+VwZqrRTBurp0n6G+n0j8UHfWjSrAqN4yUHl+heT0HpnLR2FE9YgZEmxR -bPfVbPBef/eJeE7/U6imfBYzzMajua+hg05sVHUNNdPaFOP+Xj47x8uQmf9w5/kf -MrylRUSgH5RRge4+2T5hmNM9tHfF6OfDHitrXnl+X6h/x/tkBvDcUXtKa5xuEcSg -WpmJKl3pKfXvdmCIrj9Vca+UD2Bntkk2jgDTLEPJAxMgrsQRhnUJclaunnd1NQbc -FmjFW7iaNvDVKt+vYqH8ff8U9iCB2g== +AAABMB0GA1UdDgQWBBQtvGpvrghOSrZI4B1ieqKJc69RVDANBgkqhkiG9w0BAQsF +AAOCAQEAm33hxRyoocCNH2OUhzogkSbi59+an4bLV07uTm3thrdhNKefsPlt1D6Q +11QTafhH8RAjlnn/PavLgk3WpTvuC4imlPfhxjf1gRg0zJXW3y0GC2wizSm5cXdK +L1ikKpLd17Gys4kGwMLqxCZunpGQnoJpEWBrg3kc7RPp6rgLRMAuj/ZwUzV9nNiU +YUA64FYY4ISIZigYGCkJUpqgyUQYsD+lYWZl2QlVje1riXIPReIBxd4kcMLqng0l +IysTAnZ+vNMWVLjhthGlOfs0yDaIoDa2HHskdaGtlz97SPfIUlDJ/20QLDMZEv0M +uSjtuu8PGy+iMI5+SfBE9D4cF1tn0w== -----END CERTIFICATE----- diff --git a/github/actions/testdata/leaf.key b/github/actions/testdata/leaf.key index 0fee2113..a0212231 100644 --- a/github/actions/testdata/leaf.key +++ b/github/actions/testdata/leaf.key @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDHwI/xSLgCuQrx -+WsvupA8w4eMdSefWGw523OJYPJkDYJGgSGsjVb9htba6vXYbGNohuluEAZIyT7G -vmPezTokeVMkuSYTlSV8xplFEtDlQhTzaI/cofbi7qtT91/5zS/w0JSaNosThGtZ -g/M4ZOiMj04m0NGKZz56l9Lpe/yM7fPda++D9xYEGSSdwK9CqqwF+cXN09d6IK1V -INIIjT3Sdb9SssokGWmD7UUPLvwZ5379+HRs1K8AFXqvbkeWVYtrJwJMxJGVnNSe -iqKGSmMEpP7tVNHls4V7oyQXd8KX+HpziiayjGy9giVteJJi/bAmUp+0+hTHBes5 -fOWI5JyDAgMBAAECggEADanzbrrds3n68LByD5LAeRea9xWwfyrqRE7pqVUTX5q4 -9Z+xsP7+G1uU6Oa3qHVJm4XXA+tesq3peGjfpgb92i7ebB2qKB7EsLNZGqt91KDf -lALsDFib7cwLtjOuwgSyKdPqxl3Cx7QAL+Bhy9LDQZIv82HHY6NKV9J43/XWQcGK -KNZyS1o0vBWvt135YeE3qfQA9Ww8GI3jWyk49QDOVtVNZ1HRQpXPkt5exAyU8JpW -Y3Y5VqyEcKPBRlw/scEc5CRuzIP3P06Y+NEuuvnlnDt/BR/wyyuPiZoIqGXhXUKe -oDzEmtVrvB5RmhRc1PoS1l1GBfBfVqwkLUmeSitn8QKBgQD1pC6Ukiqtm7vyhirU -ynE+Dik/gHBBW6fQalD7yZwStid1+HfIvoU+RB0wyPUVwbu7eOoMnjG2ChLYX7cV -UhAcu5ZWFhlc8OS5bGGMI99d4ueBqTYONqnCV3DhiWZhs8OesiF3hohE6jV3G5xC -ra8DzImMpujyMIWXvOwnPLRWCQKBgQDQLPwTvx472XbY1aWviQkzxMgxP8l9D+lO -nBaybarLxDa+89RMWidgJX2kGM7i6FgicGgSNpNDIYNLrVZYAz11PP6o2Oqa9ZaR -5IvnfhP1iOwSgIoC8weNSE+Y0Lw/w0IOW71+XsfgswhJG45eXV8hRqYpUEkjjl1x -nQM0hCshKwKBgCQDFfEiHK+nDT7Y/J6Fr2Rxnwp4QfzS+x9K9uRzAjacDdz1uFnt -1Ir0YXMtgwDVjjhF2cpPxunxQCIIpkax6TrNJZUpWD6P8nhcs1BgUfbptRcFP6+F -xA2B1EK8ag4Y1K0HYHCtgHzZ+Uyk95uu6uGbsu6z6aLYCj3crKJz+9xBAoGACOdT -pLiQ33hul9mTa42N4jPxaAHVaU7r6JvOcLU2D98FhGdDVjyo4HjaBdG1z4imdFqg -aN8Cr2VYiz0Pq1YAI+qG7cvRRO1qEjVXMoB29BJ2Hlh3Dqc8VHOaS+vpkUSVp62O -zj/ZhqfBm/bcwPZ3YiH2a1/usOGe54QSpgVdHt8CgYBr6xpKQSrQvgtiuv9kCzI+ -WQYP2Xxj+zsQb29hagXY/JllKOl5aDGz2qMV2RgOWZabB/xxkAeTF4A26wWrmcq0 -wsv96jEl1MtI3lB3bi/8Y/tctkNsp0drvDZdfnGMpzxDafECKdxdFvhO63p7yBOA -LG1OvFTywkxBuOUKsNsErQ== +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDf2xz5c/Kts7iY +FbznIBqnos0OZw1mXqQRfd8Ub7mcRcNu5LAS+QXaMCQrtSS8HaiOWrnmXVnY0Dtm +9BYYgxCLbffBjPgvCjMURftuI0fvFfFHhNOeuO1Y6N3z4YFY9UHldiqBIKmzrEKl +6ZzibbHhIWlOFdMCIHosnO+9LgXgEcr242yrBczl2SWycXG4OGag4yp2FrsvrKtd +qyErJBuyIQHv51k6Eo5vz42UxZCcjBciSe/X2Tf+KDLzslZDfnY+8PWU2Dx5DaMZ +AxNVa78jRDhKact9jOunKd1JRj/GZkV5EKL/lIJJQPgcNCl0s8swKlicEYpqEdS4 +Ayj0fF7DAgMBAAECggEAEoAkRPuC4KQCfmzUsV25kwXFIH3k1INZPoOigA8ujfTe +dRfb82pDd3S647/ZrhBd1PIFL73V7Jf9DzVFOp5hkiky+WFb3sT4ue60tZwF2Gnk +VUHr6WmLXoM6wLdwF+vt5JE5BXX8TUSdBEnddOf8jciHA8FeMIYcAsh8Y1932qMG +OoSMQPKgpR4Ii62GsufLrzhfShatQXWikO/O5/DaOWlOgye9CMF0JA/f5OnwqRa4 +qKQGlYrRbXtxBhjOjUJ4t0g3bTKuOhYpPdDQLf9AZqdS29fD89p+dtKvzf1eQlGZ +siPvPTSRvWjvzmEbqau1tPIGtp+fsxG2s1l05zu3yQKBgQD2QZW1i/PrVT1k4ueY +gweM9Iaj1J40URyUmgTh2aLWBGB+fHSYzYVlabpogtQ6FoNCJBOJ2nPARbcoIrin +6ArEDS0ih5H0l5h6ky/lXjIftqhiKcpf8yr82Y5xlVML0nTG7q/S/3AEWGdAiom3 +JPGGcEAMwmEsVWeVmldqMy8L+wKBgQDotqDLPMk8TTW9G/2SkuhYvmi+TkR+50/e +fgBU99E+6rZtEqqkiIwAnwHF5zCtvWWEEqaHMKoQfB1D5WgKvN0LrGfsJhQnxUT/ +uZT6uIblXcpCX+2QJqM4AYedkEkK+GNdIvnFiMU0QFibooFsaMD124Qp8yE5o9MW +5TXftCz12QKBgQDSHc3OtRawEvAql80CfbLAm5gSTAWdICRhyzUlPBzLvbpT0n9u +LdaHXHo4WIEa+0CN+9hMBIBFa3bHITEr2zrsY03Cq6hm6b6pv9LbQ89C7giokX3i +9soUKtF2qvHMIYuQffK590gt/PxEvrCY+0W17ytHd8eQG2Mh7CpwLDIVZwKBgBRU +J9W5OKcQ45nigRM1zCA8ee/t7GPdlweLoEHL6DaD0G3UzJ1U5E4FleUQ3hq/Scfw +zSVQJfsMrI3K03zkpLapV+a0KNyUigOA1bfVOMt+lvWXZaxLA8UXtR4ZhZf0vNFd +Bc6wtPFskZD+8CllOT4A8BmuCH/9hKoiWcNx/N95AoGAevwj2U2/TVQdlfTe2WDO +G/rT8nEtohcbrQyYm+yLySsKDB9AY6pPJPTW7lWfz9oXAPYeyCErtQYSIiT0OodX +EdRRmynKaRYBPxL4XNU+3oHKjuMF5+Eip+KJrONseik4ubYVQvU6DbX/xbFpyFbQ +PEj26Z+E4YTsviOuWu7EkYQ= -----END PRIVATE KEY----- diff --git a/github/actions/testdata/rootCA.crt b/github/actions/testdata/rootCA.crt index a6b6bda0..c35768d9 100644 --- a/github/actions/testdata/rootCA.crt +++ b/github/actions/testdata/rootCA.crt @@ -1,19 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIDHzCCAgegAwIBAgIUUmc9nWf4fhGFNd0oCNE0CzOXMaEwDQYJKoZIhvcNAQEL -BQAwFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTI1MDIyODEyMDEyOVoXDTI3 -MTIxOTEyMDEyOVowFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw5ZFoDGTL0YyrwEA7qTu32cur0sQXDV86Xwl -G0ilk2DXXH4F70ruTTW3NG0Rniw/rt2jzJADo1Tlosq9eJKQGQKAr21N5kjhlU3J -8nFBK+1WJyG27EvyeqZOCucXOJaAm0HSbhlT0MYpZ4kzuxmOUPmTsJmt2BtK+uRU -3LlXtzyZnJo53azQuLZz26tBGd9LXsBUMi+KJ0eX1HPluIT3o+nslnJZaqGySLKm -cJnLf9hio+rAwFBb8sgDdzeI7jqZ2bmAGPJBYpIT/dIxuZUkgTfX+OMp2g3RnQea -M0w0UjhbbQeAJONH9HGREDdp7tYtuyuBbE4miNTyjSsouqk6AwIDAQABo2MwYTAd -BgNVHQ4EFgQUfd/q0BY4fkVBV3X+HWzXH0toW08wHwYDVR0jBBgwFoAUfd/q0BY4 -fkVBV3X+HWzXH0toW08wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw -DQYJKoZIhvcNAQELBQADggEBAGLUya6xwaCwUPWHNOjlvGBGzGwAmSstJzh5o77O -XTTvyCwb0p80AnS9XoX3An5e4ePzw69mEw6RzfVLIex7fCRDekqPFuSWjVagKGJc -G7nvCqdHoCh2z1Jkb9gFpYPd6p45dtLWBw9e9/t9cFHtDR6stC16/Hy8cLzEIr0c -EWxCNdJdZW+soJivaZQeVWtlMXxVpGIs8i33CAFYufZCTKMgyYRegZuMQ676OcDE -9VSi2vJnnhdn7OBip82xX3NDQrwVt60fvFMr25cPOlzhXRY4mQLslGOleqT3sSPV -DVJnOBBmdjgFQQ8BO7rFUNGGOaUcEZp0HLRwxPZyc6OBCIg= +MIIDHzCCAgegAwIBAgIUZ73if59oOqwiywGUXBS/jjpuy9UwDQYJKoZIhvcNAQEL +BQAwFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTI2MDcxMzEzMjQwN1oXDTI5 +MDUwMjEzMjQwN1owFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr/vu/k0DNiXYF1x5g5j6Y2db4C4Q4GB7XD0L +nIbiACMrOKNS91tkQFhCIL3bKsDEL6lq/g2wP15/qDcBLZHH5exOO854hT2A4XAp +Lx7X/TGGkKhXCHV3/XX9qCep5plIpp26JHYkirXary69TWjmyLn+NJEkx0xN1jtp +V4TlXZQQUMzxPF4Aa8bbnm6AoL0qPcwjSGXiGWnNZw2I33Km0oLHrmposMCwwbSa +nu+B+UVEbJLz5wbt9psailE8QP9rNo7jTOrOQxOdc0RUP3VUubQ1MNrMbCMxdSQ2 +Fcp8A4wpD9W6Ju9MxNP3P9YgDCA0bcTXsaFFPDZHdhfzMdeN5wIDAQABo2MwYTAd +BgNVHQ4EFgQUvdo7QuZUxixdWGUVTFVO5aOKrWwwHwYDVR0jBBgwFoAUvdo7QuZU +xixdWGUVTFVO5aOKrWwwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw +DQYJKoZIhvcNAQELBQADggEBAF1gZ900iPIwG2QT39yFDm/hrKJc5flpVKj9t3jx +sLwv3mnxMixL8Id0lhTPVD7a1PbqWhNu9reVFmYdstWDoo1tJBjAGKqHKf7XgI1Z +6nyxJnUuy5xjuKIV+i8Lwb5jVCSfDE0mbx2AomJB6t4bCtntIWBF/e6K9C6WgQqe +rFXOvB5WSEhyJPZUNJc364Zt7xhMT3i6soIkR8SEPuOkikP3HbqSKd4XHQYEFp3J +2J6v/suZtXz0K+BLDmD4HLN5xviD+kXaaZ5EBtRl681f9F6gjirKukB2tcZ11gU9 +rTQSVvjbbiY19frSWeRKAaYt14x56EvKQoqDwHZTRK/qQv4= -----END CERTIFICATE----- diff --git a/github/actions/testdata/server.crt b/github/actions/testdata/server.crt index 60930054..11ed1396 100644 --- a/github/actions/testdata/server.crt +++ b/github/actions/testdata/server.crt @@ -1,20 +1,20 @@ -----BEGIN CERTIFICATE----- -MIIDOjCCAiKgAwIBAgIUQr7R8yN5+2and6ucUOPF6oIbD48wDQYJKoZIhvcNAQEL -BQAwFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTI1MDIyODEyMDEzMFoXDTI2 -MDcxMzEyMDEzMFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0B -AQEFAAOCAQ8AMIIBCgKCAQEA4oL2hAPQlDVaNJru5fIstkpoVSuam0vpswC7ciRc -XQRjF3q8kjtIA7+jdySsKJqOLGnybDX3awvRyKMEjq11IfnZLjZc+FzTlA+x4z0h -MHb0GiBFXKNzrExGI9F0KEPtFxcMIqZ119LY2ReexxWkZBQYlgTepaevp71za4c2 -n4Zy1+0iS5+uklZ4ANKMTBGlN76Qgt530VnpNiIeUbiUzY58Vx4q7kFcUv/oSz8p -rbXr+/GGpAjrOc6/JsezRE8YK2po60dvV80TJ2Jt6pduvF7OSQnq/v4mJl1xuXKl -Byo9HLbeu3BuVRWQs2/EwEzx5kX3Ugysl9Bm44K2yKe9/QIDAQABo4GAMH4wHwYD -VR0jBBgwFoAUfd/q0BY4fkVBV3X+HWzXH0toW08wCQYDVR0TBAIwADALBgNVHQ8E +MIIDOjCCAiKgAwIBAgIUVy/UETGamzkzSYS3TortnzNWCeowDQYJKoZIhvcNAQEL +BQAwFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTI2MDcxMzEzMjQwN1oXDTI3 +MTEyNTEzMjQwN1owFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0B +AQEFAAOCAQ8AMIIBCgKCAQEAuzF9o1Wc2r1sjIeJRtjzquwqfbOBVefd1264YeSc +cR/V8TNaI7KWo9dftWt7P2t6zysw6DzPcwh//+iCuvEAyo1uXd+jwPQyJJsrjC5f +pLSltk6BV/qObXGqJtqdj7mGkrlRWUhLzxyU4KqrSoiQTNZisON++vrjPiIADqFv +EWrA9aEsaa0HYXGwQCLmuCwPd3CxQldPX3yzGSI0SRj/StkTAq94wFx2OrGPeJ2N +PKbftMifHG+OUXpl3J4N0xAP5A8vEXCFDwJIzOnY0a5fxKLlcfHB1oArscuNU61G +gzYkRgEr+B3NrX1zT01lzbIPXZM39MxUZJkgzFUNAZgBNQIDAQABo4GAMH4wHwYD +VR0jBBgwFoAUvdo7QuZUxixdWGUVTFVO5aOKrWwwCQYDVR0TBAIwADALBgNVHQ8E BAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0RBAgwBocEfwAAATAdBgNV -HQ4EFgQUe0rTTfWjho3hgeLTnajTCpddo2MwDQYJKoZIhvcNAQELBQADggEBAIR2 -5zkA7rPnddxCunsz8Jjq3wyhR/KiAFz+RGeFeiXDkF2fWr7QIQ9KbFbv8tpfXR7P -B75bY0sXwutHMB2sZDi92cH5sthNBfp19fI35cxcU4oTPxp4UZJKEiA3Qx8y73CX -NJu1009nPdOJNlIboDGAFdZ5SH6RCh+YcQZ68kjHPWBIpXxLbs9FN3QmpbAvtLh1 -PoPaSy7IjKmxm1u+Lf6tyIn2IiB3MiynaB3OKvbkLCseM/5SZKMk6WKSDWopOCJr -xciPOc+yeLz5I2Omn0uViOIIciqjlgxncWAyNtDgvJcecwqB2cPiIhk6GY0QZ1uM -e7KoqGzWXvWLqJ13a9U= +HQ4EFgQUAHxI5UMg4SIhBu/GMKrTaX6Ir+IwDQYJKoZIhvcNAQELBQADggEBACkv +3Y89Xwn4PM+PTUa3VyrvMVOqaOtbfSV9I1Duf2/CQ2er3LdJHgH8VjDKc3lRNQMF +F6c9FTQWs0SoYe3KufKgt52f/hLkU0AS42eps5TKXitznAs/5NNh0Oom6Vv/V+H+ +CIOs+KrekzQ1Uu8f8XJHmjP9zQ4bQ+7+9uZZxHsE4P6pkz9gf896qL8ZjJGnEB8C +xelSm+MX3iCmDG2s3RZrMSjRRHiluylcliftwY4OlbuMrBXS1Of3N9nzhpePYQbM +Pcy5mIO5t5HGiPeRJ30/JKuNM55wP/wNbSicUQNc+x4nkVq/AMKKCtyiLs8aAhZu +IzEG5rEh7Ypi6s+ic8s= -----END CERTIFICATE----- diff --git a/github/actions/testdata/server.key b/github/actions/testdata/server.key index 064c6c12..61a9dcca 100644 --- a/github/actions/testdata/server.key +++ b/github/actions/testdata/server.key @@ -1,28 +1,28 @@ -----BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDigvaEA9CUNVo0 -mu7l8iy2SmhVK5qbS+mzALtyJFxdBGMXerySO0gDv6N3JKwomo4safJsNfdrC9HI -owSOrXUh+dkuNlz4XNOUD7HjPSEwdvQaIEVco3OsTEYj0XQoQ+0XFwwipnXX0tjZ -F57HFaRkFBiWBN6lp6+nvXNrhzafhnLX7SJLn66SVngA0oxMEaU3vpCC3nfRWek2 -Ih5RuJTNjnxXHiruQVxS/+hLPymttev78YakCOs5zr8mx7NETxgramjrR29XzRMn -Ym3ql268Xs5JCer+/iYmXXG5cqUHKj0ctt67cG5VFZCzb8TATPHmRfdSDKyX0Gbj -grbIp739AgMBAAECggEADgUIbbAFbJbyHV1q5Jqc/9oSeRW40lyG0Mh+fEMZ4Gam -x3ZA+QAS+1W/hV6ktTf+YsCv+4NKQWWQN3iM41PYcyDmu1XWt/Hu5TQk0NQgxhd8 -EP3nAnkvbf5OkmWiveHuaRvJFCqfZ/Cp8U3lSvHg+edwhMs1CKXHWSeAXwBrIMEb -ajpxuD3B/NT/CGmKnj3cgAuIbvNHVIcwu8ACbpczDL++vi7KrWmOJn1QzSlUlNFi -fsgnF0heO5Uff4vkjXU84INQxOP3tbvXcDNiwDewZy75h2d3Pv+ku8GoZYWFUXSJ -yKtafJMJUD0kJMuKhkzrwYcQGY6ioSYisPK+JoungQKBgQD8fWmuHwCXbM4Ckyns -Wg4f+kG8d+wypgIs6ENmgr9UnNB0N6n7nO7v/4l/l7IN9CQQmdtSvek2ytk5rGBM -XUAWxZaokE3MecxR0EUJx42k/k3dN4XgU/YNk4D6/wpEsyUATE4nIFDVjxE+Jc07 -CZ2CUWKyxTPGz2kfHnEQ0vFiYQKBgQDlqRiGlJ6c99zTas2wrvr+50aQhn6BryDK -kjGM6woPnnwMq+Jy6vum3o0cU+iNNeFAijShXo2XR3iZJcoJ2sPhy3dRWdBNdFyy -hwxgD0cXzEjQL0M03DPDykTnM7ZvE6KUZjnxJZkytJHLKapoGzxBH9656zx5qnuH -MPYwTWg5HQKBgDKBD4OBtgeT/v0q3KbnOI4S69U8E6Xp6ON8rgayPn05RMUKYVjw -AidFcQZxnG8IF7KuY92AGUcZeiv8G+MKgAhOC526B6XP8xumUjjrjpyjNYX7Vi8R -/FSo3ZLXMwGc59jQao2O/DxLesJ4oz2c5cGsb9acdYfd8wQDfdBEsX3hAoGAc4Pu -NiMi9MknZZ/e/fPFg9lIgQFlOE2iLMID8mF2mgyZULZUHIFdOr3ONGVwHzbuqcva -VSB+D41/d2iuiu5igHwa8+w8/fh9d7691sNYevvh0/Ux1LC9yMlAhxpXtN8nc4VH -t6e1uu9gNdQrRloMoKUrHlDYBkpd/838xqbouXECgYEArwn+eXKD5zgNN4jEbNBp -ygIp+Oh2abt+CNQjfLUa+qon5ziH53mHixJ2hpaOa6Rxxu9R2ZgNLtbodm+ccD8z -ZNA7Z0rApAwfuhD8zIzkZ4HuARN8eopYmTubpzDkAcfRWhw1EBDQc0V6trl+EJsK -xfbmGepRVWXw2dLmxhA9/zM= +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7MX2jVZzavWyM +h4lG2POq7Cp9s4FV593Xbrhh5JxxH9XxM1ojspaj11+1a3s/a3rPKzDoPM9zCH// +6IK68QDKjW5d36PA9DIkmyuMLl+ktKW2ToFX+o5tcaom2p2PuYaSuVFZSEvPHJTg +qqtKiJBM1mKw4376+uM+IgAOoW8RasD1oSxprQdhcbBAIua4LA93cLFCV09ffLMZ +IjRJGP9K2RMCr3jAXHY6sY94nY08pt+0yJ8cb45RemXcng3TEA/kDy8RcIUPAkjM +6djRrl/EouVx8cHWgCuxy41TrUaDNiRGASv4Hc2tfXNPTWXNsg9dkzf0zFRkmSDM +VQ0BmAE1AgMBAAECggEAGpnI0cobtRXP9BRalJ/F2Jcg3qYAjhWZ98A7IWYcORMe +Et2KRKJfN78mUaCJbhS26fOoSohBroYZsmL3GNugkigQ8/QQ0B2jdoI6aPw6CSzG +k6aKSNdJIBJT7JuKYl8p2XXOMpLMew0Dtv1pw/iuEM0yTxK0+7NlUXIBchLMtrun +O5krr85tDZQfTQzWzo6HfYv5tbyCyYHrQmXP24vh9UIAV32tahx3lfR2u3tZPVsE +7FWueR523AwcnaMtK3d40RGLceCsO9vYrOu2sLkTadtp0YfnFxDw90slarolE3AY +tl8OrDMDFFtOM7E+25CvV9aRTbkh0PPMuZ0b5zV4AQKBgQD1Qy4zvcOe4cKZJcH+ +vB0wnUuq2BwpqombSPY8h5pHuJgcovWHlD6QGNwpqPhq06skkce4+cN/LE1Opchs +ZEuBAK+mzg+FXqiLKdPRfEiEVapxWFLLW0IVrDUV+rjRl8FAxmKeY3b4iAfhNs3B +y3dG3hLSyjXF0xF4ZVtLdDvWAQKBgQDDY329L+iBRAd4DNKoSHB0B6cX59HaI/vJ +KPUfDfKRKoNwxDk+ZKnu+Mq9o2LsIObG8nE42otLI3H1Yf9A4CPaVEIiDKxzl9Qh +1sDvYQ81tRksd3llghYW5R7tRsUEXbGiT35O0UW8V3M/DZnScSFw/OgF1BHpB9nN +XORUDpKzNQKBgD36IQXAMVvH7vXmXbRycunjBckBXXSF/Mn50QeQuMLg+cdawrLl +Gj4tmg0I/uV34aqXECJFM0rCtjJmoFQLsCzEMJ71OWTZe7vW8Qjh4C1YvH5x9FIh +GH0DjFKmjeeg8pvcxfUCa4D4Vw1pXOtY8CY/XgBYSuhXovWKn1xc3fYBAoGBAKVi +ZH8ruyEXpT7Pk8wPrOf5xkzs02tSxAAMpBcKJkUL+EAOR+nxKQn3Gx0T3FrEqrLB +ambWTctnEEHFFlsinhZi9at+kMYzQML31U2KGWa7iLhj1KTATNRKUjhDi+ZbLE2X +/fDXILEAULORXp+LVOQfnoG4dQSLuacjtr0TrMb1AoGAXd/MNTpw3U1QiaY9Qhm/ +kH5j+5mkOe0zs520mLCQW0bV1l2MiW+4TIACDS8Ok7Ln89N3heeDcmLXecCNBMn2 +5peofJj0fVhHNWMjmFWdZr2Gzy3OP4nHY+pLoLJylgq99zSWL2EwtAo2Yje/Omz+ +8aUFUg2z34K+VpDf4D9qWd4= -----END PRIVATE KEY----- diff --git a/main.go b/main.go index 80c047e6..72c65f6b 100644 --- a/main.go +++ b/main.go @@ -340,6 +340,7 @@ func main() { Client: mgr.GetClient(), Log: log.WithName("EphemeralRunner").WithValues("version", build.Version), Scheme: mgr.GetScheme(), + PublishMetrics: metricsAddr != "0", ResourceBuilder: rb, }).SetupWithManager(mgr, runnerOpts...); err != nil { log.Error(err, "unable to create controller", "controller", "EphemeralRunner") @@ -350,7 +351,6 @@ func main() { Client: mgr.GetClient(), Log: log.WithName("EphemeralRunnerSet").WithValues("version", build.Version), Scheme: mgr.GetScheme(), - PublishMetrics: metricsAddr != "0", ResourceBuilder: rb, }).SetupWithManager(mgr, controllerOpts...); err != nil { log.Error(err, "unable to create controller", "controller", "EphemeralRunnerSet")