Skip to content

Commit 786b6b5

Browse files
committed
Only update machine condition LastTransitionTime when the status changes
Signed-off-by: Lukas Frank <lukas.frank@sap.com>
1 parent 975a4d1 commit 786b6b5

2 files changed

Lines changed: 85 additions & 55 deletions

File tree

poollet/machinepoollet/controllers/machine_controller.go

Lines changed: 24 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"google.golang.org/grpc/status"
1616

1717
"github.com/ironcore-dev/controller-utils/clientutils"
18+
"github.com/ironcore-dev/controller-utils/conditionutils"
1819
commonv1alpha1 "github.com/ironcore-dev/ironcore/api/common/v1alpha1"
1920
computev1alpha1 "github.com/ironcore-dev/ironcore/api/compute/v1alpha1"
2021
ipamv1alpha1 "github.com/ironcore-dev/ironcore/api/ipam/v1alpha1"
@@ -502,43 +503,42 @@ func (r *MachineReconciler) updateMachineStatus(ctx context.Context, machine *co
502503
machine.Status.ObservedGeneration = generation
503504
machine.Status.Volumes = volumeStatuses
504505
machine.Status.NetworkInterfaces = nicStatuses
505-
machine.Status.Conditions = r.computeMachineConditions(state, volumeStatuses, nicStatuses, now)
506+
if err := ComputeMachineConditions(&machine.Status.Conditions, state, volumeStatuses, nicStatuses); err != nil {
507+
return fmt.Errorf("error computing machine conditions: %w", err)
508+
}
506509

507510
if err := r.Status().Patch(ctx, machine, client.MergeFrom(base)); err != nil {
508511
return fmt.Errorf("error patching status: %w", err)
509512
}
510513
return nil
511514
}
512515

513-
// computeMachineConditions computes the conditions for the machine based on its current state.
514-
func (r *MachineReconciler) computeMachineConditions(
516+
func ComputeMachineConditions(
517+
conditions *[]computev1alpha1.MachineCondition,
515518
state computev1alpha1.MachineState,
516519
volumeStatuses []computev1alpha1.VolumeStatus,
517520
nicStatuses []computev1alpha1.NetworkInterfaceStatus,
518-
now metav1.Time,
519-
) []computev1alpha1.MachineCondition {
520-
var conditions []computev1alpha1.MachineCondition
521-
522-
conditions = append(conditions, r.computeMachineReadyCondition(state, now))
523-
521+
) error {
522+
desired := []computev1alpha1.MachineCondition{computeMachineReadyCondition(state)}
524523
if len(volumeStatuses) > 0 {
525-
if c := r.computeVolumesReadyCondition(volumeStatuses, now); c.Type != "" {
526-
conditions = append(conditions, c)
527-
}
524+
desired = append(desired, computeVolumesReadyCondition(volumeStatuses))
528525
}
529-
530526
if len(nicStatuses) > 0 {
531-
if c := r.computeNetworkInterfacesReadyCondition(nicStatuses, now); c.Type != "" {
532-
conditions = append(conditions, c)
533-
}
527+
desired = append(desired, computeNetworkInterfacesReadyCondition(nicStatuses))
534528
}
535529

536-
return conditions
530+
for _, cond := range desired {
531+
if err := conditionutils.UpdateSlice(conditions, string(cond.Type),
532+
conditionutils.UpdateFromCondition{Condition: cond},
533+
); err != nil {
534+
return fmt.Errorf("error updating %s condition: %w", cond.Type, err)
535+
}
536+
}
537+
return nil
537538
}
538539

539-
func (r *MachineReconciler) computeMachineReadyCondition(state computev1alpha1.MachineState, now metav1.Time) computev1alpha1.MachineCondition {
540+
func computeMachineReadyCondition(state computev1alpha1.MachineState) computev1alpha1.MachineCondition {
540541
status, reason, message := corev1.ConditionFalse, "NotReady", "Machine is not ready"
541-
542542
switch state {
543543
case computev1alpha1.MachineStateRunning:
544544
status, reason, message = corev1.ConditionTrue, "Running", "Machine is running"
@@ -547,23 +547,11 @@ func (r *MachineReconciler) computeMachineReadyCondition(state computev1alpha1.M
547547
case computev1alpha1.MachineStateTerminating, computev1alpha1.MachineStateTerminated:
548548
status, reason, message = corev1.ConditionFalse, "Terminating", "Machine is terminating or terminated"
549549
}
550-
551-
return computev1alpha1.MachineCondition{
552-
Type: "Ready",
553-
Status: status,
554-
Reason: reason,
555-
Message: message,
556-
LastTransitionTime: now,
557-
}
550+
return computev1alpha1.MachineCondition{Type: "Ready", Status: status, Reason: reason, Message: message}
558551
}
559552

560-
func (r *MachineReconciler) computeVolumesReadyCondition(volumeStatuses []computev1alpha1.VolumeStatus, now metav1.Time) computev1alpha1.MachineCondition {
561-
if len(volumeStatuses) == 0 {
562-
return computev1alpha1.MachineCondition{}
563-
}
564-
553+
func computeVolumesReadyCondition(volumeStatuses []computev1alpha1.VolumeStatus) computev1alpha1.MachineCondition {
565554
status, reason, message := corev1.ConditionTrue, "VolumesReady", "All volumes are ready"
566-
567555
for _, vs := range volumeStatuses {
568556
if vs.State != computev1alpha1.VolumeStateAttached {
569557
status = corev1.ConditionFalse
@@ -572,23 +560,11 @@ func (r *MachineReconciler) computeVolumesReadyCondition(volumeStatuses []comput
572560
break
573561
}
574562
}
575-
576-
return computev1alpha1.MachineCondition{
577-
Type: computev1alpha1.MachineConditionType("VolumesReady"),
578-
Status: status,
579-
Reason: reason,
580-
Message: message,
581-
LastTransitionTime: now,
582-
}
563+
return computev1alpha1.MachineCondition{Type: "VolumesReady", Status: status, Reason: reason, Message: message}
583564
}
584565

585-
func (r *MachineReconciler) computeNetworkInterfacesReadyCondition(nicStatuses []computev1alpha1.NetworkInterfaceStatus, now metav1.Time) computev1alpha1.MachineCondition {
586-
if len(nicStatuses) == 0 {
587-
return computev1alpha1.MachineCondition{}
588-
}
589-
566+
func computeNetworkInterfacesReadyCondition(nicStatuses []computev1alpha1.NetworkInterfaceStatus) computev1alpha1.MachineCondition {
590567
status, reason, message := corev1.ConditionTrue, "NetworkInterfacesReady", "All network interfaces are ready"
591-
592568
for _, nicStatus := range nicStatuses {
593569
if nicStatus.State != computev1alpha1.NetworkInterfaceStateAttached {
594570
status = corev1.ConditionFalse
@@ -597,14 +573,7 @@ func (r *MachineReconciler) computeNetworkInterfacesReadyCondition(nicStatuses [
597573
break
598574
}
599575
}
600-
601-
return computev1alpha1.MachineCondition{
602-
Type: computev1alpha1.MachineConditionType("NetworkInterfacesReady"),
603-
Status: status,
604-
Reason: reason,
605-
Message: message,
606-
LastTransitionTime: now,
607-
}
576+
return computev1alpha1.MachineCondition{Type: "NetworkInterfacesReady", Status: status, Reason: reason, Message: message}
608577
}
609578

610579
func (r *MachineReconciler) prepareIRIPower(power computev1alpha1.Power) (iri.Power, error) {

poollet/machinepoollet/controllers/machine_controller_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package controllers_test
66
import (
77
"encoding/json"
88
"fmt"
9+
"time"
910

1011
. "github.com/afritzler/protoequal"
1112
commonv1alpha1 "github.com/ironcore-dev/ironcore/api/common/v1alpha1"
@@ -18,6 +19,7 @@ import (
1819
testingmachine "github.com/ironcore-dev/ironcore/iri/testing/machine"
1920
poolletutils "github.com/ironcore-dev/ironcore/poollet/common/utils"
2021
machinepoolletv1alpha1 "github.com/ironcore-dev/ironcore/poollet/machinepoollet/api/v1alpha1"
22+
"github.com/ironcore-dev/ironcore/poollet/machinepoollet/controllers"
2123
. "github.com/onsi/ginkgo/v2"
2224
. "github.com/onsi/gomega"
2325
. "github.com/onsi/gomega/gstruct"
@@ -1147,3 +1149,62 @@ func mustMarshalJSON(v interface{}) string {
11471149
}
11481150
return string(data)
11491151
}
1152+
1153+
var _ = Describe("ComputeMachineConditions", func() {
1154+
findCondition := func(conditions []computev1alpha1.MachineCondition, typ computev1alpha1.MachineConditionType) *computev1alpha1.MachineCondition {
1155+
for i := range conditions {
1156+
if conditions[i].Type == typ {
1157+
return &conditions[i]
1158+
}
1159+
}
1160+
return nil
1161+
}
1162+
1163+
It("preserves a condition's LastTransitionTime when its status does not change", func() {
1164+
old := metav1.NewTime(time.Now().Add(-time.Hour))
1165+
conditions := []computev1alpha1.MachineCondition{
1166+
{Type: "Ready", Status: corev1.ConditionTrue, Reason: "Running", Message: "Machine is running", LastTransitionTime: old},
1167+
}
1168+
1169+
Expect(controllers.ComputeMachineConditions(&conditions, computev1alpha1.MachineStateRunning, nil, nil)).To(Succeed())
1170+
1171+
ready := findCondition(conditions, "Ready")
1172+
Expect(ready).NotTo(BeNil())
1173+
Expect(ready.Status).To(Equal(corev1.ConditionTrue))
1174+
Expect(ready.LastTransitionTime.Time).To(BeTemporally("==", old.Time))
1175+
})
1176+
1177+
It("bumps a condition's LastTransitionTime when its status changes", func() {
1178+
old := metav1.NewTime(time.Now().Add(-time.Hour))
1179+
conditions := []computev1alpha1.MachineCondition{
1180+
{Type: "Ready", Status: corev1.ConditionTrue, Reason: "Running", Message: "Machine is running", LastTransitionTime: old},
1181+
}
1182+
1183+
Expect(controllers.ComputeMachineConditions(&conditions, computev1alpha1.MachineStateTerminating, nil, nil)).To(Succeed())
1184+
1185+
ready := findCondition(conditions, "Ready")
1186+
Expect(ready).NotTo(BeNil())
1187+
Expect(ready.Status).To(Equal(corev1.ConditionFalse))
1188+
Expect(ready.Reason).To(Equal("Terminating"))
1189+
Expect(ready.LastTransitionTime.Time).To(BeTemporally(">", old.Time))
1190+
})
1191+
1192+
It("adds volume and network interface conditions and preserves their timestamps on an unchanged recompute", func() {
1193+
var conditions []computev1alpha1.MachineCondition
1194+
volumeStatuses := []computev1alpha1.VolumeStatus{{Name: "root", State: computev1alpha1.VolumeStateAttached}}
1195+
nicStatuses := []computev1alpha1.NetworkInterfaceStatus{{Name: "nic", State: computev1alpha1.NetworkInterfaceStateAttached}}
1196+
1197+
Expect(controllers.ComputeMachineConditions(&conditions, computev1alpha1.MachineStateRunning, volumeStatuses, nicStatuses)).To(Succeed())
1198+
1199+
for _, typ := range []computev1alpha1.MachineConditionType{"Ready", "VolumesReady", "NetworkInterfacesReady"} {
1200+
cond := findCondition(conditions, typ)
1201+
Expect(cond).NotTo(BeNil(), "condition %s should be present", typ)
1202+
Expect(cond.Status).To(Equal(corev1.ConditionTrue))
1203+
Expect(cond.LastTransitionTime.IsZero()).To(BeFalse())
1204+
}
1205+
1206+
volumesBefore := findCondition(conditions, "VolumesReady").LastTransitionTime
1207+
Expect(controllers.ComputeMachineConditions(&conditions, computev1alpha1.MachineStateRunning, volumeStatuses, nicStatuses)).To(Succeed())
1208+
Expect(findCondition(conditions, "VolumesReady").LastTransitionTime.Time).To(BeTemporally("==", volumesBefore.Time))
1209+
})
1210+
})

0 commit comments

Comments
 (0)