|
| 1 | +package conditions |
| 2 | + |
| 3 | +import ( |
| 4 | + "slices" |
| 5 | + "strings" |
| 6 | + "time" |
| 7 | + |
| 8 | + "k8s.io/apimachinery/pkg/util/sets" |
| 9 | +) |
| 10 | + |
| 11 | +// conditionUpdater is a helper struct for updating a list of Conditions. |
| 12 | +// Use the ConditionUpdater constructor for initializing. |
| 13 | +type conditionUpdater[T comparable] struct { |
| 14 | + Now time.Time |
| 15 | + conditions map[string]Condition[T] |
| 16 | + updated sets.Set[string] |
| 17 | + constructor func() Condition[T] |
| 18 | + changed bool |
| 19 | +} |
| 20 | + |
| 21 | +// ConditionUpdater creates a builder-like helper struct for updating a list of Conditions. |
| 22 | +// The 'constructor' argument is a function that returns a new (empty) instance of the condition implementation type. |
| 23 | +// The 'conditions' argument contains the old condition list. |
| 24 | +// If removeUntouched is true, the condition list returned with Conditions() will have all conditions removed that have not been updated. |
| 25 | +// If false, all conditions will be kept. |
| 26 | +// Note that calling this function stores the current time as timestamp that is used as timestamp if a condition's status changed. |
| 27 | +// To overwrite this timestamp, modify the 'Now' field of the returned struct manually. |
| 28 | +// |
| 29 | +// The given condition list is not modified. |
| 30 | +// |
| 31 | +// Usage example: |
| 32 | +// status.conditions = ConditionUpdater(status.conditions, true).UpdateCondition(...).UpdateCondition(...).Conditions() |
| 33 | +func ConditionUpdater[T comparable](constructor func() Condition[T], conditions []Condition[T], removeUntouched bool) *conditionUpdater[T] { |
| 34 | + res := &conditionUpdater[T]{ |
| 35 | + Now: time.Now(), |
| 36 | + conditions: make(map[string]Condition[T], len(conditions)), |
| 37 | + constructor: constructor, |
| 38 | + changed: false, |
| 39 | + } |
| 40 | + for _, con := range conditions { |
| 41 | + res.conditions[con.GetType()] = con |
| 42 | + } |
| 43 | + if removeUntouched { |
| 44 | + res.updated = sets.New[string]() |
| 45 | + } |
| 46 | + return res |
| 47 | +} |
| 48 | + |
| 49 | +// UpdateCondition updates or creates the condition with the specified type. |
| 50 | +// All fields of the condition are updated with the values given in the arguments, but the condition's LastTransitionTime is only updated (with the timestamp contained in the receiver struct) if the status changed. |
| 51 | +// Returns the receiver for easy chaining. |
| 52 | +func (c *conditionUpdater[T]) UpdateCondition(conType string, status T, reason, message string) *conditionUpdater[T] { |
| 53 | + con := c.constructor() |
| 54 | + con.SetType(conType) |
| 55 | + con.SetStatus(status) |
| 56 | + con.SetReason(reason) |
| 57 | + con.SetMessage(message) |
| 58 | + con.SetLastTransitionTime(c.Now) |
| 59 | + old, ok := c.conditions[conType] |
| 60 | + if ok && old.GetStatus() == con.GetStatus() { |
| 61 | + // update LastTransitionTime only if status changed |
| 62 | + con.SetLastTransitionTime(old.GetLastTransitionTime()) |
| 63 | + } |
| 64 | + if !c.changed { |
| 65 | + if ok { |
| 66 | + c.changed = old.GetStatus() != con.GetStatus() || old.GetReason() != con.GetReason() || old.GetMessage() != con.GetMessage() |
| 67 | + } else { |
| 68 | + c.changed = true |
| 69 | + } |
| 70 | + } |
| 71 | + c.conditions[conType] = con |
| 72 | + if c.updated != nil { |
| 73 | + c.updated.Insert(conType) |
| 74 | + } |
| 75 | + return c |
| 76 | +} |
| 77 | + |
| 78 | +// UpdateConditionFromTemplate is a convenience wrapper around UpdateCondition which allows it to be called with a preconstructed ComponentCondition. |
| 79 | +func (c *conditionUpdater[T]) UpdateConditionFromTemplate(con Condition[T]) *conditionUpdater[T] { |
| 80 | + return c.UpdateCondition(con.GetType(), con.GetStatus(), con.GetReason(), con.GetMessage()) |
| 81 | +} |
| 82 | + |
| 83 | +// HasCondition returns true if a condition with the given type exists in the updated condition list. |
| 84 | +func (c *conditionUpdater[T]) HasCondition(conType string) bool { |
| 85 | + _, ok := c.conditions[conType] |
| 86 | + return ok && (c.updated == nil || c.updated.Has(conType)) |
| 87 | +} |
| 88 | + |
| 89 | +// RemoveCondition removes the condition with the given type from the updated condition list. |
| 90 | +func (c *conditionUpdater[T]) RemoveCondition(conType string) *conditionUpdater[T] { |
| 91 | + if !c.HasCondition(conType) { |
| 92 | + return c |
| 93 | + } |
| 94 | + delete(c.conditions, conType) |
| 95 | + if c.updated != nil { |
| 96 | + c.updated.Delete(conType) |
| 97 | + } |
| 98 | + c.changed = true |
| 99 | + return c |
| 100 | +} |
| 101 | + |
| 102 | +// Conditions returns the updated condition list. |
| 103 | +// If the condition updater was initialized with removeUntouched=true, this list will only contain the conditions which have been updated |
| 104 | +// in between the condition updater creation and this method call. Otherwise, it will potentially also contain old conditions. |
| 105 | +// The conditions are returned sorted by their type. |
| 106 | +func (c *conditionUpdater[T]) Conditions() ([]Condition[T], bool) { |
| 107 | + res := make([]Condition[T], 0, len(c.conditions)) |
| 108 | + for _, con := range c.conditions { |
| 109 | + if c.updated == nil { |
| 110 | + res = append(res, con) |
| 111 | + continue |
| 112 | + } |
| 113 | + if c.updated.Has(con.GetType()) { |
| 114 | + res = append(res, con) |
| 115 | + } else { |
| 116 | + c.changed = true |
| 117 | + } |
| 118 | + } |
| 119 | + slices.SortStableFunc(res, func(a, b Condition[T]) int { |
| 120 | + return strings.Compare(a.GetType(), b.GetType()) |
| 121 | + }) |
| 122 | + return res, c.changed |
| 123 | +} |
0 commit comments