1 package conditions
2
3 import (
4 "fmt"
5 "sort"
6 "time"
7
8 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9
10 "edge-infra.dev/pkg/k8s/meta/status"
11 )
12
13
14
15 type Setter interface {
16 Getter
17 SetConditions([]metav1.Condition)
18 }
19
20 func UniqueConditions(conditions []metav1.Condition) []metav1.Condition {
21 uniqueConditions := make([]metav1.Condition, 0)
22 keys := make(map[string]bool)
23 for _, condition := range conditions {
24 if _, exists := keys[condition.Type]; !exists {
25 keys[condition.Type] = true
26 uniqueConditions = append(uniqueConditions, condition)
27 }
28 }
29 return uniqueConditions
30 }
31
32
33
34
35
36
37 func Set(to Setter, condition *metav1.Condition) {
38 if to == nil || condition == nil {
39 return
40 }
41
42
43 condition.ObservedGeneration = to.GetGeneration()
44
45
46
47 conditions := to.GetConditions()
48 exists := false
49 for i := range conditions {
50 existingCondition := conditions[i]
51 if existingCondition.Type == condition.Type {
52 exists = true
53 if !hasSameState(&existingCondition, condition) {
54 condition.LastTransitionTime = metav1.NewTime(time.Now().UTC().Truncate(time.Second))
55 conditions[i] = *condition
56 break
57 }
58 condition.LastTransitionTime = existingCondition.LastTransitionTime
59
60
61 if existingCondition.ObservedGeneration != condition.ObservedGeneration {
62 conditions[i] = *condition
63 }
64 break
65 }
66 }
67
68
69
70 if !exists {
71 if condition.LastTransitionTime.IsZero() {
72 condition.LastTransitionTime = metav1.NewTime(time.Now().UTC().Truncate(time.Second))
73 }
74 conditions = append(conditions, *condition)
75 }
76
77
78 sort.Slice(conditions, func(i, j int) bool {
79 return lexicographicLess(&conditions[i], &conditions[j])
80 })
81
82 to.SetConditions(conditions)
83 }
84
85
86
87 func TrueCondition(t, reason, messageFormat string, messageArgs ...interface{}) *metav1.Condition {
88 return &metav1.Condition{
89 Type: t,
90 Status: metav1.ConditionTrue,
91 Reason: reason,
92 Message: fmt.Sprintf(messageFormat, messageArgs...),
93 }
94 }
95
96
97
98 func FalseCondition(t, reason, messageFormat string, messageArgs ...interface{}) *metav1.Condition {
99 return &metav1.Condition{
100 Type: t,
101 Status: metav1.ConditionFalse,
102 Reason: reason,
103 Message: fmt.Sprintf(messageFormat, messageArgs...),
104 }
105 }
106
107
108
109 func UnknownCondition(t, reason, messageFormat string, messageArgs ...interface{}) *metav1.Condition {
110 return &metav1.Condition{
111 Type: t,
112 Status: metav1.ConditionUnknown,
113 Reason: reason,
114 Message: fmt.Sprintf(messageFormat, messageArgs...),
115 }
116 }
117
118
119
120 func MarkTrue(to Setter, t, reason, messageFormat string, messageArgs ...interface{}) {
121 Set(to, TrueCondition(t, reason, messageFormat, messageArgs...))
122 }
123
124
125
126 func MarkUnknown(to Setter, t, reason, messageFormat string, messageArgs ...interface{}) {
127 Set(to, UnknownCondition(t, reason, messageFormat, messageArgs...))
128 }
129
130
131
132 func MarkFalse(to Setter, t, reason, messageFormat string, messageArgs ...interface{}) {
133 Set(to, FalseCondition(t, reason, messageFormat, messageArgs...))
134 }
135
136
137
138
139
140
141
142 func MarkReconciling(to Setter, reason, messageFormat string, messageArgs ...interface{}) {
143 Delete(to, status.StalledCondition)
144 MarkTrue(to, status.ReconcilingCondition, reason, messageFormat, messageArgs...)
145 }
146
147
148
149
150
151
152
153
154
155 func MarkStalled(to Setter, reason, messageFormat string, messageArgs ...interface{}) {
156 Delete(to, status.ReconcilingCondition)
157 MarkTrue(to, status.StalledCondition, reason, messageFormat, messageArgs...)
158 }
159
160
161
162
163 func SetSummary(to Setter, targetCondition string, options ...MergeOption) {
164 Set(to, summary(to, targetCondition, options...))
165 }
166
167
168
169
170 func SetMirror(to Setter, targetCondition string, from Getter, options ...MirrorOptions) {
171 Set(to, mirror(from, targetCondition, options...))
172 }
173
174
175
176
177
178 func SetAggregate(to Setter, targetCondition string, from []Getter, options ...MergeOption) {
179 Set(to, aggregate(from, targetCondition, options...))
180 }
181
182
183 func Delete(to Setter, t string) {
184 if to == nil {
185 return
186 }
187
188 conditions := to.GetConditions()
189 newConditions := make([]metav1.Condition, 0, len(conditions))
190 for _, condition := range conditions {
191 if condition.Type != t {
192 newConditions = append(newConditions, condition)
193 }
194 }
195 to.SetConditions(newConditions)
196 }
197
198
199
200 var conditionWeights = map[string]int{
201 status.StalledCondition: 0,
202 status.ReconcilingCondition: 1,
203 status.ReadyCondition: 2,
204 }
205
206
207
208
209
210
211 func lexicographicLess(i, j *metav1.Condition) bool {
212 w1, ok1 := conditionWeights[i.Type]
213 w2, ok2 := conditionWeights[j.Type]
214 switch {
215 case ok1 && ok2:
216 return w1 < w2
217 case ok1, ok2:
218 return !ok2
219 case i.ObservedGeneration == j.ObservedGeneration:
220 return i.Type < j.Type
221 default:
222 return i.ObservedGeneration > j.ObservedGeneration
223 }
224 }
225
226
227
228
229 func hasSameState(i, j *metav1.Condition) bool {
230 return i.Type == j.Type &&
231 i.Status == j.Status &&
232 i.Reason == j.Reason &&
233 i.Message == j.Message
234 }
235
View as plain text