diff --git a/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go b/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go index 1ae599f1fb..96742e0701 100644 --- a/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go +++ b/apis/actions.github.com/v1alpha1/autoscalinglistener_types.go @@ -89,7 +89,17 @@ func (s *AutoscalingListenerSpec) Hash() string { } // AutoscalingListenerStatus defines the observed state of AutoscalingListener -type AutoscalingListenerStatus struct{} +type AutoscalingListenerStatus struct { + // ObservedGeneration is the most recent generation observed by the controller. + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // Conditions describe the current state of the AutoscalingListener. + // +optional + // +listType=map + // +listMapKey=type + Conditions []metav1.Condition `json:"conditions,omitempty"` +} // +kubebuilder:object:root=true // +kubebuilder:subresource:status diff --git a/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go b/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go index 24ccc8e328..8783f57161 100644 --- a/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go +++ b/apis/actions.github.com/v1alpha1/autoscalingrunnerset_types.go @@ -314,6 +314,16 @@ type HistogramMetric struct { // AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet type AutoscalingRunnerSetStatus struct { + // ObservedGeneration is the most recent generation observed by the controller. + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // Conditions describe the current state of the AutoscalingRunnerSet. + // +optional + // +listType=map + // +listMapKey=type + Conditions []metav1.Condition `json:"conditions,omitempty"` + // +optional CurrentRunners int `json:"currentRunners"` diff --git a/apis/actions.github.com/v1alpha1/common.go b/apis/actions.github.com/v1alpha1/common.go index 6a3a59976a..1531160081 100644 --- a/apis/actions.github.com/v1alpha1/common.go +++ b/apis/actions.github.com/v1alpha1/common.go @@ -1,5 +1,8 @@ package v1alpha1 +// ConditionTypeReady is the condition type reported in the status of all resources in this API group. +const ConditionTypeReady = "Ready" + // ResourceMeta carries metadata common to all internal resources type ResourceMeta struct { // +optional diff --git a/apis/actions.github.com/v1alpha1/ephemeralrunner_types.go b/apis/actions.github.com/v1alpha1/ephemeralrunner_types.go index 6c2c03150e..a5aabe3b72 100644 --- a/apis/actions.github.com/v1alpha1/ephemeralrunner_types.go +++ b/apis/actions.github.com/v1alpha1/ephemeralrunner_types.go @@ -135,6 +135,16 @@ func (s *EphemeralRunnerSpec) Hash() string { // EphemeralRunnerStatus defines the observed state of EphemeralRunner type EphemeralRunnerStatus struct { + // ObservedGeneration is the most recent generation observed by the controller. + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // Conditions describe the current state of the EphemeralRunner. + // +optional + // +listType=map + // +listMapKey=type + Conditions []metav1.Condition `json:"conditions,omitempty"` + // Turns true only if the runner is online. // +optional Ready bool `json:"ready"` diff --git a/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go b/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go index bfcd424d84..0859aee311 100644 --- a/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go +++ b/apis/actions.github.com/v1alpha1/ephemeralrunnerset_types.go @@ -37,6 +37,16 @@ type EphemeralRunnerSetSpec struct { // EphemeralRunnerSetStatus defines the observed state of EphemeralRunnerSet type EphemeralRunnerSetStatus struct { + // ObservedGeneration is the most recent generation observed by the controller. + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // Conditions describe the current state of the EphemeralRunnerSet. + // +optional + // +listType=map + // +listMapKey=type + Conditions []metav1.Condition `json:"conditions,omitempty"` + // CurrentReplicas is the number of currently running EphemeralRunner resources being managed by this EphemeralRunnerSet. CurrentReplicas int `json:"currentReplicas"` // +optional diff --git a/apis/actions.github.com/v1alpha1/zz_generated.deepcopy.go b/apis/actions.github.com/v1alpha1/zz_generated.deepcopy.go index df2dabc81a..a28e3b3a54 100644 --- a/apis/actions.github.com/v1alpha1/zz_generated.deepcopy.go +++ b/apis/actions.github.com/v1alpha1/zz_generated.deepcopy.go @@ -32,7 +32,7 @@ func (in *AutoscalingListener) DeepCopyInto(out *AutoscalingListener) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoscalingListener. @@ -153,6 +153,13 @@ func (in *AutoscalingListenerSpec) DeepCopy() *AutoscalingListenerSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AutoscalingListenerStatus) DeepCopyInto(out *AutoscalingListenerStatus) { *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoscalingListenerStatus. @@ -171,7 +178,7 @@ func (in *AutoscalingRunnerSet) DeepCopyInto(out *AutoscalingRunnerSet) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoscalingRunnerSet. @@ -323,6 +330,13 @@ func (in *AutoscalingRunnerSetSpec) DeepCopy() *AutoscalingRunnerSetSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AutoscalingRunnerSetStatus) DeepCopyInto(out *AutoscalingRunnerSetStatus) { *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoscalingRunnerSetStatus. @@ -435,7 +449,7 @@ func (in *EphemeralRunnerSet) DeepCopyInto(out *EphemeralRunnerSet) { out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EphemeralRunnerSet. @@ -512,6 +526,13 @@ func (in *EphemeralRunnerSetSpec) DeepCopy() *EphemeralRunnerSetSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EphemeralRunnerSetStatus) DeepCopyInto(out *EphemeralRunnerSetStatus) { *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EphemeralRunnerSetStatus. @@ -563,6 +584,13 @@ func (in *EphemeralRunnerSpec) DeepCopy() *EphemeralRunnerSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EphemeralRunnerStatus) DeepCopyInto(out *EphemeralRunnerStatus) { *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.Failures != nil { in, out := &in.Failures, &out.Failures *out = make(map[string]metav1.Time, len(*in)) 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..b33ed0202d 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 @@ -8807,6 +8807,72 @@ spec: type: object status: description: AutoscalingListenerStatus defines the observed state of AutoscalingListener + properties: + conditions: + description: Conditions describe the current state of the AutoscalingListener. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + observedGeneration: + description: ObservedGeneration is the most recent generation observed + by the controller. + format: int64 + type: integer type: object type: object served: true 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..37135f47de 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 @@ -16543,10 +16543,73 @@ spec: status: description: AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet properties: + conditions: + description: Conditions describe the current state of the AutoscalingRunnerSet. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map currentRunners: type: integer failedEphemeralRunners: type: integer + observedGeneration: + description: ObservedGeneration is the most recent generation observed by the controller. + format: int64 + type: integer pendingEphemeralRunners: type: integer phase: 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..5b379fb5ba 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 @@ -8298,6 +8298,65 @@ spec: status: description: EphemeralRunnerStatus defines the observed state of EphemeralRunner properties: + conditions: + description: Conditions describe the current state of the EphemeralRunner. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map failures: additionalProperties: format: date-time @@ -8316,6 +8375,10 @@ spec: type: string message: type: string + observedGeneration: + description: ObservedGeneration is the most recent generation observed by the controller. + format: int64 + type: integer phase: description: |- Phase describes phases where EphemeralRunner can be in. 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..465f7e099f 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 @@ -8313,11 +8313,74 @@ spec: status: description: EphemeralRunnerSetStatus defines the observed state of EphemeralRunnerSet properties: + conditions: + description: Conditions describe the current state of the EphemeralRunnerSet. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map currentReplicas: description: CurrentReplicas is the number of currently running EphemeralRunner resources being managed by this EphemeralRunnerSet. type: integer failedEphemeralRunners: type: integer + observedGeneration: + description: ObservedGeneration is the most recent generation observed by the controller. + format: int64 + type: integer pendingEphemeralRunners: type: integer phase: 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..b33ed0202d 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 @@ -8807,6 +8807,72 @@ spec: type: object status: description: AutoscalingListenerStatus defines the observed state of AutoscalingListener + properties: + conditions: + description: Conditions describe the current state of the AutoscalingListener. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + observedGeneration: + description: ObservedGeneration is the most recent generation observed + by the controller. + format: int64 + 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 82001c24f4..37135f47de 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 @@ -16543,10 +16543,73 @@ spec: status: description: AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet properties: + conditions: + description: Conditions describe the current state of the AutoscalingRunnerSet. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map currentRunners: type: integer failedEphemeralRunners: type: integer + observedGeneration: + description: ObservedGeneration is the most recent generation observed by the controller. + format: int64 + type: integer pendingEphemeralRunners: type: integer phase: 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..5b379fb5ba 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 @@ -8298,6 +8298,65 @@ spec: status: description: EphemeralRunnerStatus defines the observed state of EphemeralRunner properties: + conditions: + description: Conditions describe the current state of the EphemeralRunner. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map failures: additionalProperties: format: date-time @@ -8316,6 +8375,10 @@ spec: type: string message: type: string + observedGeneration: + description: ObservedGeneration is the most recent generation observed by the controller. + format: int64 + type: integer phase: description: |- Phase describes phases where EphemeralRunner can be in. 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..465f7e099f 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 @@ -8313,11 +8313,74 @@ spec: status: description: EphemeralRunnerSetStatus defines the observed state of EphemeralRunnerSet properties: + conditions: + description: Conditions describe the current state of the EphemeralRunnerSet. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map currentReplicas: description: CurrentReplicas is the number of currently running EphemeralRunner resources being managed by this EphemeralRunnerSet. type: integer failedEphemeralRunners: type: integer + observedGeneration: + description: ObservedGeneration is the most recent generation observed by the controller. + format: int64 + type: integer pendingEphemeralRunners: type: integer phase: diff --git a/config/crd/bases/actions.github.com_autoscalinglisteners.yaml b/config/crd/bases/actions.github.com_autoscalinglisteners.yaml index 20e57e3398..b33ed0202d 100644 --- a/config/crd/bases/actions.github.com_autoscalinglisteners.yaml +++ b/config/crd/bases/actions.github.com_autoscalinglisteners.yaml @@ -8807,6 +8807,72 @@ spec: type: object status: description: AutoscalingListenerStatus defines the observed state of AutoscalingListener + properties: + conditions: + description: Conditions describe the current state of the AutoscalingListener. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + observedGeneration: + description: ObservedGeneration is the most recent generation observed + by the controller. + format: int64 + 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 82001c24f4..37135f47de 100644 --- a/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml +++ b/config/crd/bases/actions.github.com_autoscalingrunnersets.yaml @@ -16543,10 +16543,73 @@ spec: status: description: AutoscalingRunnerSetStatus defines the observed state of AutoscalingRunnerSet properties: + conditions: + description: Conditions describe the current state of the AutoscalingRunnerSet. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map currentRunners: type: integer failedEphemeralRunners: type: integer + observedGeneration: + description: ObservedGeneration is the most recent generation observed by the controller. + format: int64 + type: integer pendingEphemeralRunners: type: integer phase: diff --git a/config/crd/bases/actions.github.com_ephemeralrunners.yaml b/config/crd/bases/actions.github.com_ephemeralrunners.yaml index 6cc6707ab2..5b379fb5ba 100644 --- a/config/crd/bases/actions.github.com_ephemeralrunners.yaml +++ b/config/crd/bases/actions.github.com_ephemeralrunners.yaml @@ -8298,6 +8298,65 @@ spec: status: description: EphemeralRunnerStatus defines the observed state of EphemeralRunner properties: + conditions: + description: Conditions describe the current state of the EphemeralRunner. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map failures: additionalProperties: format: date-time @@ -8316,6 +8375,10 @@ spec: type: string message: type: string + observedGeneration: + description: ObservedGeneration is the most recent generation observed by the controller. + format: int64 + type: integer phase: description: |- Phase describes phases where EphemeralRunner can be in. diff --git a/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml b/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml index ae035cfbf7..465f7e099f 100644 --- a/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml +++ b/config/crd/bases/actions.github.com_ephemeralrunnersets.yaml @@ -8313,11 +8313,74 @@ spec: status: description: EphemeralRunnerSetStatus defines the observed state of EphemeralRunnerSet properties: + conditions: + description: Conditions describe the current state of the EphemeralRunnerSet. + items: + description: Condition contains details for one aspect of the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map currentReplicas: description: CurrentReplicas is the number of currently running EphemeralRunner resources being managed by this EphemeralRunnerSet. type: integer failedEphemeralRunners: type: integer + observedGeneration: + description: ObservedGeneration is the most recent generation observed by the controller. + format: int64 + type: integer pendingEphemeralRunners: type: integer phase: diff --git a/controllers/actions.github.com/autoscalinglistener_controller.go b/controllers/actions.github.com/autoscalinglistener_controller.go index 7d12f895d6..9d878b8c98 100644 --- a/controllers/actions.github.com/autoscalinglistener_controller.go +++ b/controllers/actions.github.com/autoscalinglistener_controller.go @@ -24,7 +24,9 @@ import ( "time" "github.com/go-logr/logr" + apiequality "k8s.io/apimachinery/pkg/api/equality" kerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" @@ -543,7 +545,7 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl. case cs == nil: log.Info("Listener pod is not ready", "namespace", listenerPod.Namespace, "name", listenerPod.Name) - return ctrl.Result{}, nil + return ctrl.Result{}, r.updateStatus(ctx, &autoscalingListener, metav1.ConditionFalse, "PodNotReady", "Listener pod is not ready", log) case cs.State.Terminated != nil: log.Info( "Listener pod is terminated", @@ -563,12 +565,29 @@ func (r *AutoscalingListenerReconciler) Reconcile(ctx context.Context, req ctrl. // notify the reconciler again. return ctrl.Result{}, nil } - return ctrl.Result{}, nil + return ctrl.Result{}, r.updateStatus(ctx, &autoscalingListener, metav1.ConditionTrue, "PodRunning", "Listener pod is running", log) } return ctrl.Result{}, nil } +func (r *AutoscalingListenerReconciler) updateStatus(ctx context.Context, autoscalingListener *v1alpha1.AutoscalingListener, status metav1.ConditionStatus, reason, message string, log logr.Logger) error { + original := autoscalingListener.DeepCopy() + autoscalingListener.Status.ObservedGeneration = autoscalingListener.Generation + setReadyCondition(&autoscalingListener.Status.Conditions, autoscalingListener.Generation, status, reason, message) + + if apiequality.Semantic.DeepEqual(original.Status, autoscalingListener.Status) { + return nil + } + + if err := r.Status().Patch(ctx, autoscalingListener, client.MergeFrom(original)); err != nil { + log.Error(err, "Failed to patch autoscaling listener status") + return err + } + + return nil +} + func (r *AutoscalingListenerReconciler) deleteListenerPod(ctx context.Context, autoscalingListener *v1alpha1.AutoscalingListener, listenerPod *corev1.Pod, log logr.Logger) error { if err := r.publishRunningListener(autoscalingListener, false); err != nil { log.Error(err, "Unable to publish runner listener down metric", "namespace", listenerPod.Namespace, "name", listenerPod.Name) diff --git a/controllers/actions.github.com/autoscalingrunnerset_controller.go b/controllers/actions.github.com/autoscalingrunnerset_controller.go index a07cb35083..c85f0384ef 100644 --- a/controllers/actions.github.com/autoscalingrunnerset_controller.go +++ b/controllers/actions.github.com/autoscalingrunnerset_controller.go @@ -31,7 +31,9 @@ import ( "github.com/google/go-cmp/cmp" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" + apiequality "k8s.io/apimachinery/pkg/api/equality" kerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" @@ -156,6 +158,13 @@ func (r *AutoscalingRunnerSetReconciler) Reconcile(ctx context.Context, req ctrl original = autoscalingRunnerSet.DeepCopy() autoscalingRunnerSet.Status.Phase = v1alpha1.AutoscalingRunnerSetPhasePending + setReadyCondition( + &autoscalingRunnerSet.Status.Conditions, + autoscalingRunnerSet.Generation, + metav1.ConditionFalse, + string(v1alpha1.AutoscalingRunnerSetPhasePending), + "AutoscalingRunnerSet spec changed; waiting for the runner scale set and listener to be updated", + ) if err := r.Status().Patch(ctx, &autoscalingRunnerSet, client.MergeFrom(original)); err != nil { log.Error(err, "Failed to update autoscaling runner set status with pending phase") return ctrl.Result{}, err @@ -433,23 +442,34 @@ 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 - phaseDiff := phase != autoscalingRunnerSet.Status.Phase - if !countDiff && !phaseDiff { - return nil - } - original := autoscalingRunnerSet.DeepCopy() - if phaseDiff { - autoscalingRunnerSet.Status.Phase = phase - } - if countDiff && ephemeralRunnerSet != nil { + autoscalingRunnerSet.Status.Phase = phase + if 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.ObservedGeneration = autoscalingRunnerSet.Generation + + readyStatus := metav1.ConditionFalse + message := "AutoscalingRunnerSet is not ready" + if phase == v1alpha1.AutoscalingRunnerSetPhaseRunning { + readyStatus = metav1.ConditionTrue + message = "Runner scale set, ephemeral runner set and listener are up to date" + } + setReadyCondition( + &autoscalingRunnerSet.Status.Conditions, + autoscalingRunnerSet.Generation, + readyStatus, + string(phase), + message, + ) + + if apiequality.Semantic.DeepEqual(original.Status, autoscalingRunnerSet.Status) { + return nil + } 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 55ebe5c6ae..eace0cef37 100644 --- a/controllers/actions.github.com/autoscalingrunnerset_controller_test.go +++ b/controllers/actions.github.com/autoscalingrunnerset_controller_test.go @@ -21,6 +21,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -894,12 +895,23 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { if err != nil { return v1alpha1.AutoscalingRunnerSetStatus{}, fmt.Errorf("failed to get AutoScalingRunnerSet: %w", err) } - return updated.Status, nil + status := updated.Status + status.Conditions = nil + status.ObservedGeneration = 0 + return status, nil }, autoscalingRunnerSetTestTimeout, autoscalingRunnerSetTestInterval, ).Should(BeEquivalentTo(desiredStatus), "AutoScalingRunnerSet status should be updated") + updatedRunnerSet := new(v1alpha1.AutoscalingRunnerSet) + err = k8sClient.Get(ctx, client.ObjectKey{Name: autoscalingRunnerSet.Name, Namespace: autoscalingRunnerSet.Namespace}, updatedRunnerSet) + Expect(err).NotTo(HaveOccurred(), "failed to get AutoScalingRunnerSet") + Expect(updatedRunnerSet.Status.ObservedGeneration).To(BeEquivalentTo(updatedRunnerSet.Generation), "ObservedGeneration should match the generation") + readyCondition := meta.FindStatusCondition(updatedRunnerSet.Status.Conditions, v1alpha1.ConditionTypeReady) + Expect(readyCondition).NotTo(BeNil(), "Ready condition should be set") + Expect(readyCondition.Status).To(BeEquivalentTo(metav1.ConditionTrue), "Ready condition should be true") + // Patch the AutoScalingRunnerSet image which should trigger // the recreation of the Listener and EphemeralRunnerSet patched := autoscalingRunnerSet.DeepCopy() @@ -1001,7 +1013,10 @@ var _ = Describe("Test AutoScalingRunnerSet controller", Ordered, func() { if err != nil { return v1alpha1.AutoscalingRunnerSetStatus{}, fmt.Errorf("failed to get AutoScalingRunnerSet: %w", err) } - return updated.Status, nil + status := updated.Status + status.Conditions = nil + status.ObservedGeneration = 0 + return status, nil }, autoscalingRunnerSetTestTimeout, autoscalingRunnerSetTestInterval, diff --git a/controllers/actions.github.com/conditions.go b/controllers/actions.github.com/conditions.go new file mode 100644 index 0000000000..0ca2cf3d82 --- /dev/null +++ b/controllers/actions.github.com/conditions.go @@ -0,0 +1,18 @@ +package actionsgithubcom + +import ( + "github.com/actions/actions-runner-controller/apis/actions.github.com/v1alpha1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// setReadyCondition updates the Ready condition and reports whether it changed. +func setReadyCondition(conditions *[]metav1.Condition, generation int64, status metav1.ConditionStatus, reason, message string) bool { + return meta.SetStatusCondition(conditions, metav1.Condition{ + Type: v1alpha1.ConditionTypeReady, + Status: status, + ObservedGeneration: generation, + Reason: reason, + Message: message, + }) +} diff --git a/controllers/actions.github.com/ephemeralrunner_controller.go b/controllers/actions.github.com/ephemeralrunner_controller.go index 64b98e2745..fab9ea07d8 100644 --- a/controllers/actions.github.com/ephemeralrunner_controller.go +++ b/controllers/actions.github.com/ephemeralrunner_controller.go @@ -28,6 +28,7 @@ import ( "github.com/actions/scaleset" "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" + apiequality "k8s.io/apimachinery/pkg/api/equality" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -580,6 +581,8 @@ func (r *EphemeralRunnerReconciler) markAsFailed(ctx context.Context, ephemeralR ephemeralRunner.Status.Phase = v1alpha1.EphemeralRunnerPhaseFailed ephemeralRunner.Status.Reason = reason ephemeralRunner.Status.Message = errMessage + ephemeralRunner.Status.ObservedGeneration = ephemeralRunner.Generation + setReadyCondition(&ephemeralRunner.Status.Conditions, ephemeralRunner.Generation, metav1.ConditionFalse, reason, errMessage) 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) } @@ -600,6 +603,8 @@ func (r *EphemeralRunnerReconciler) markAsOutdated(ctx context.Context, ephemera ephemeralRunner.Status.Phase = v1alpha1.EphemeralRunnerPhaseOutdated ephemeralRunner.Status.Reason = "Outdated" ephemeralRunner.Status.Message = "Runner is deprecated" + ephemeralRunner.Status.ObservedGeneration = ephemeralRunner.Generation + setReadyCondition(&ephemeralRunner.Status.Conditions, ephemeralRunner.Generation, metav1.ConditionFalse, "Outdated", "Runner is deprecated") 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) @@ -633,6 +638,8 @@ func (r *EphemeralRunnerReconciler) deletePodAsFailed(ctx context.Context, ephem ephemeralRunner.Status.Ready = false ephemeralRunner.Status.Reason = pod.Status.Reason ephemeralRunner.Status.Message = pod.Status.Message + ephemeralRunner.Status.ObservedGeneration = ephemeralRunner.Generation + setReadyCondition(&ephemeralRunner.Status.Conditions, ephemeralRunner.Generation, metav1.ConditionFalse, "PodFailed", "Runner pod failed and will be restarted") if err := r.Status().Patch(ctx, ephemeralRunner, client.MergeFrom(original)); err != nil { return fmt.Errorf("failed to update ephemeral runner status with failure count: %w", err) @@ -811,11 +818,24 @@ func (r *EphemeralRunnerReconciler) updateRunStatusFromPod(ctx context.Context, } } - phase := v1alpha1.EphemeralRunnerPhase(pod.Status.Phase) - phaseChanged := ephemeralRunner.Status.Phase != phase - readyChanged := ready != ephemeralRunner.Status.Ready + original := ephemeralRunner.DeepCopy() + ephemeralRunner.Status.Phase = v1alpha1.EphemeralRunnerPhase(pod.Status.Phase) + ephemeralRunner.Status.Ready = ready + ephemeralRunner.Status.Reason = pod.Status.Reason + ephemeralRunner.Status.Message = pod.Status.Message + ephemeralRunner.Status.ObservedGeneration = ephemeralRunner.Generation + + readyStatus := metav1.ConditionFalse + reason := "RunnerOffline" + message := "Runner pod is not ready" + if ready { + readyStatus = metav1.ConditionTrue + reason = "RunnerOnline" + message = "Runner is online and ready to execute a job" + } + setReadyCondition(&ephemeralRunner.Status.Conditions, ephemeralRunner.Generation, readyStatus, reason, message) - if !phaseChanged && !readyChanged { + if apiequality.Semantic.DeepEqual(original.Status, ephemeralRunner.Status) { return nil } @@ -826,11 +846,6 @@ func (r *EphemeralRunnerReconciler) updateRunStatusFromPod(ctx context.Context, "statusMessage", pod.Status.Message, "ready", ready, ) - original := ephemeralRunner.DeepCopy() - ephemeralRunner.Status.Phase = phase - ephemeralRunner.Status.Ready = ready - 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 runner status for Phase/Reason/Message/Ready: %w", err) diff --git a/controllers/actions.github.com/ephemeralrunnerset_controller.go b/controllers/actions.github.com/ephemeralrunnerset_controller.go index 381c9d29de..e7b89e64fa 100644 --- a/controllers/actions.github.com/ephemeralrunnerset_controller.go +++ b/controllers/actions.github.com/ephemeralrunnerset_controller.go @@ -34,7 +34,9 @@ import ( "github.com/go-logr/logr" "go.uber.org/multierr" corev1 "k8s.io/api/core/v1" + apiequality "k8s.io/apimachinery/pkg/api/equality" kerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" @@ -282,17 +284,29 @@ func (r *EphemeralRunnerSetReconciler) updateStatus(ctx context.Context, ephemer default: phase = ephemeralRunnerSet.Status.Phase } - desiredStatus := v1alpha1.EphemeralRunnerSetStatus{ - CurrentReplicas: total, - Phase: phase, - PendingEphemeralRunners: len(state.pending), - RunningEphemeralRunners: len(state.running), - FailedEphemeralRunners: len(state.failed), - } + ephemeralRunnerSet.Status.CurrentReplicas = total + ephemeralRunnerSet.Status.Phase = phase + ephemeralRunnerSet.Status.PendingEphemeralRunners = len(state.pending) + ephemeralRunnerSet.Status.RunningEphemeralRunners = len(state.running) + ephemeralRunnerSet.Status.FailedEphemeralRunners = len(state.failed) + ephemeralRunnerSet.Status.ObservedGeneration = ephemeralRunnerSet.Generation + + readyStatus := metav1.ConditionTrue + message := "EphemeralRunnerSet is running" + if phase == v1alpha1.EphemeralRunnerSetPhaseOutdated { + readyStatus = metav1.ConditionFalse + message = "EphemeralRunnerSet contains outdated ephemeral runners" + } + setReadyCondition( + &ephemeralRunnerSet.Status.Conditions, + ephemeralRunnerSet.Generation, + readyStatus, + string(phase), + message, + ) // Update the status if needed. - if ephemeralRunnerSet.Status != desiredStatus { - ephemeralRunnerSet.Status = desiredStatus + if !apiequality.Semantic.DeepEqual(original.Status, ephemeralRunnerSet.Status) { if err := r.Status().Patch(ctx, ephemeralRunnerSet, client.MergeFrom(original)); err != nil { log.Error(err, "Failed to update EphemeralRunnerSet status") return err diff --git a/controllers/actions.github.com/ephemeralrunnerset_controller_test.go b/controllers/actions.github.com/ephemeralrunnerset_controller_test.go index c345965256..58cf27445c 100644 --- a/controllers/actions.github.com/ephemeralrunnerset_controller_test.go +++ b/controllers/actions.github.com/ephemeralrunnerset_controller_test.go @@ -14,6 +14,7 @@ import ( corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" @@ -1197,7 +1198,10 @@ var _ = Describe("Test EphemeralRunnerSet controller", func() { if err != nil { return v1alpha1.EphemeralRunnerSetStatus{}, err } - return updated.Status, nil + status := updated.Status + status.Conditions = nil + status.ObservedGeneration = 0 + return status, nil }, ephemeralRunnerSetTestTimeout, ephemeralRunnerSetTestInterval, @@ -1207,6 +1211,11 @@ var _ = Describe("Test EphemeralRunnerSet controller", func() { err = k8sClient.Get(ctx, client.ObjectKey{Name: ephemeralRunnerSet.Name, Namespace: ephemeralRunnerSet.Namespace}, updated) Expect(err).NotTo(HaveOccurred(), "Failed to fetch ephemeral runner set") + Expect(updated.Status.ObservedGeneration).To(BeEquivalentTo(updated.Generation), "ObservedGeneration should match the generation") + readyCondition := meta.FindStatusCondition(updated.Status.Conditions, v1alpha1.ConditionTypeReady) + Expect(readyCondition).NotTo(BeNil(), "Ready condition should be set") + Expect(readyCondition.Status).To(BeEquivalentTo(metav1.ConditionTrue), "Ready condition should be true") + updatedOriginal := updated.DeepCopy() updated.Spec.Replicas = 0 @@ -1241,7 +1250,10 @@ var _ = Describe("Test EphemeralRunnerSet controller", func() { if err != nil { return v1alpha1.EphemeralRunnerSetStatus{}, err } - return updated.Status, nil + status := updated.Status + status.Conditions = nil + status.ObservedGeneration = 0 + return status, nil }, ephemeralRunnerSetTestTimeout, ephemeralRunnerSetTestInterval, @@ -1264,7 +1276,10 @@ var _ = Describe("Test EphemeralRunnerSet controller", func() { if err != nil { return v1alpha1.EphemeralRunnerSetStatus{}, err } - return updated.Status, nil + status := updated.Status + status.Conditions = nil + status.ObservedGeneration = 0 + return status, nil }, ephemeralRunnerSetTestTimeout, ephemeralRunnerSetTestInterval,