/
common_cluster_status_funcs.go
355 lines (288 loc) · 11.4 KB
/
common_cluster_status_funcs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
package v1alpha2
import (
"sort"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func (s CommonClusterStatus) GetCreatedCondition() CommonClusterStatusCondition {
return getCondition(s.Conditions, ClusterStatusConditionCreated)
}
func (s CommonClusterStatus) GetCreatingCondition() CommonClusterStatusCondition {
return getCondition(s.Conditions, ClusterStatusConditionCreating)
}
func (s CommonClusterStatus) GetDeletedCondition() CommonClusterStatusCondition {
return getCondition(s.Conditions, ClusterStatusConditionDeleted)
}
func (s CommonClusterStatus) GetDeletingCondition() CommonClusterStatusCondition {
return getCondition(s.Conditions, ClusterStatusConditionDeleting)
}
func (s CommonClusterStatus) GetUpdatedCondition() CommonClusterStatusCondition {
return getCondition(s.Conditions, ClusterStatusConditionUpdated)
}
func (s CommonClusterStatus) GetUpdatingCondition() CommonClusterStatusCondition {
return getCondition(s.Conditions, ClusterStatusConditionUpdating)
}
func (s CommonClusterStatus) HasCreatedCondition() bool {
return hasCondition(s.Conditions, ClusterStatusConditionCreated)
}
func (s CommonClusterStatus) HasCreatingCondition() bool {
return hasCondition(s.Conditions, ClusterStatusConditionCreating)
}
func (s CommonClusterStatus) HasDeletedCondition() bool {
return hasCondition(s.Conditions, ClusterStatusConditionDeleted)
}
func (s CommonClusterStatus) HasDeletingCondition() bool {
return hasCondition(s.Conditions, ClusterStatusConditionDeleting)
}
func (s CommonClusterStatus) HasUpdatedCondition() bool {
return hasCondition(s.Conditions, ClusterStatusConditionUpdated)
}
func (s CommonClusterStatus) HasUpdatingCondition() bool {
return hasCondition(s.Conditions, ClusterStatusConditionUpdating)
}
func (s CommonClusterStatus) HasVersion(semver string) bool {
return hasVersion(s.Versions, semver)
}
func (s CommonClusterStatus) LatestCondition() string {
if len(s.Conditions) == 0 {
return ""
}
sort.Sort(sort.Reverse(sortClusterStatusConditionsByDate(s.Conditions)))
return s.Conditions[0].Condition
}
func (s CommonClusterStatus) LatestVersion() string {
if len(s.Versions) == 0 {
return ""
}
sort.Sort(sort.Reverse(sortClusterStatusVersionsByDate(s.Versions)))
return s.Versions[0].Version
}
func (s CommonClusterStatus) WithCreatedCondition() []CommonClusterStatusCondition {
newCondition := CommonClusterStatusCondition{
LastTransitionTime: metav1.Now(),
Condition: ClusterStatusConditionCreated,
}
return withCondition(s.Conditions, newCondition, ClusterConditionLimit)
}
func (s CommonClusterStatus) WithCreatingCondition() []CommonClusterStatusCondition {
newCondition := CommonClusterStatusCondition{
LastTransitionTime: metav1.Now(),
Condition: ClusterStatusConditionCreating,
}
return withCondition(s.Conditions, newCondition, ClusterConditionLimit)
}
func (s CommonClusterStatus) WithDeletedCondition() []CommonClusterStatusCondition {
newCondition := CommonClusterStatusCondition{
LastTransitionTime: metav1.Now(),
Condition: ClusterStatusConditionDeleted,
}
return withCondition(s.Conditions, newCondition, ClusterConditionLimit)
}
func (s CommonClusterStatus) WithDeletingCondition() []CommonClusterStatusCondition {
newCondition := CommonClusterStatusCondition{
LastTransitionTime: metav1.Now(),
Condition: ClusterStatusConditionDeleting,
}
return withCondition(s.Conditions, newCondition, ClusterConditionLimit)
}
func (s CommonClusterStatus) WithNewVersion(version string) []CommonClusterStatusVersion {
newVersion := CommonClusterStatusVersion{
LastTransitionTime: metav1.Now(),
Version: version,
}
return withVersion(s.Versions, newVersion, ClusterVersionLimit)
}
func (s CommonClusterStatus) WithUpdatedCondition() []CommonClusterStatusCondition {
newCondition := CommonClusterStatusCondition{
LastTransitionTime: metav1.Now(),
Condition: ClusterStatusConditionUpdated,
}
return withCondition(s.Conditions, newCondition, ClusterConditionLimit)
}
func (s CommonClusterStatus) WithUpdatingCondition() []CommonClusterStatusCondition {
newCondition := CommonClusterStatusCondition{
LastTransitionTime: metav1.Now(),
Condition: ClusterStatusConditionUpdating,
}
return withCondition(s.Conditions, newCondition, ClusterConditionLimit)
}
func getCondition(conditions []CommonClusterStatusCondition, condition string) CommonClusterStatusCondition {
for _, c := range conditions {
if c.Condition == condition {
return c
}
}
return CommonClusterStatusCondition{}
}
func getConditionForPair(a CommonClusterStatusCondition) string {
for _, p := range conditionPairs {
if p[0] == a.Condition {
return p[1]
}
if p[1] == a.Condition {
return p[0]
}
}
return ""
}
func hasCondition(conditions []CommonClusterStatusCondition, condition string) bool {
for _, c := range conditions {
if c.Condition == condition {
return true
}
}
return false
}
func hasVersion(versions []CommonClusterStatusVersion, search string) bool {
for _, v := range versions {
if v.Version == search {
return true
}
}
return false
}
func isConditionPair(a CommonClusterStatusCondition, b CommonClusterStatusCondition) bool {
for _, p := range conditionPairs {
if p[0] == a.Condition && p[1] == b.Condition {
return true
}
if p[1] == a.Condition && p[0] == b.Condition {
return true
}
}
return false
}
// withCondition takes a list of status conditions and manages the given list
// according to the condition to add on top and the given limit argument. The
// limit argument should always only be given by ClusterConditionLimit. Also see
// the godoc there. The limit is applied to condition pairs as defined by
// conditionPairs. Internally the given conditions list is copied so that the
// input arguments are not manipulated by accident. One specific functionality
// of withCondition is that incomplete condition pairs are completed
// automatically as this may happen due to unexpected behaviour in the callers
// environment. For more information on implementation details read the inline
// comments of the code.
func withCondition(conditions []CommonClusterStatusCondition, condition CommonClusterStatusCondition, limit int) []CommonClusterStatusCondition {
// We create a new list which acts like a copy so the input parameters are not
// manipulated. Here we also prepend the given condition and inject certain
// missing conditions in case the condition list gets out of sync
// unintendedly due to any eventual bugs. Test case 8 demonstrates that.
var newConditions []CommonClusterStatusCondition
{
if len(conditions) > 0 && conditions[0].Condition == condition.Condition {
injected := CommonClusterStatusCondition{
// The implication of unintendedly untracked conditions is that the
// automatically added condition does not obtain a reasonable timestamp.
// Here we take the timestamp of the new condition we want to track and
// substract one nano second from it to keep the order intact.
LastTransitionTime: metav1.Time{Time: condition.LastTransitionTime.Add(-(1 * time.Nanosecond))},
Condition: getConditionForPair(condition),
}
newConditions = append(newConditions, injected)
}
newConditions = append(newConditions, condition)
newConditions = append(newConditions, conditions...)
}
// The new list is sorted to have the first item being the oldest. This is to
// have an easier grouping mechanism below. When the first item of a new pair
// is added, it would throw of the grouping when the order would be kept as
// given.
sort.Sort(sortClusterStatusConditionsByDate(newConditions))
// The conditions are grouped into their corresponding pairs of transitioning
// states. Associated Creating/Created, Updating/Updated and Deleting/Deleted
// conditions are put together.
var conditionGroups [][]CommonClusterStatusCondition
for len(newConditions) > 0 {
var g []CommonClusterStatusCondition
for _, c := range newConditions {
// If the list only contains one item anymore, we process it separately
// here and be done. Otherwhise the pruning of the list below panics due
// to the range calculations.
if len(newConditions) == 1 {
g = append(g, c)
newConditions = []CommonClusterStatusCondition{}
break
}
// Put the first item from the top of the list into the group and drop
// the grouped item from the list.
if len(g) == 0 {
g = append(g, c)
newConditions = newConditions[1:]
continue
}
// When we find the second item of the pair we are done for this group.
if len(g) == 1 {
if isConditionPair(g[0], c) {
g = append(g, c)
newConditions = newConditions[1:]
}
break
}
}
conditionGroups = append(conditionGroups, g)
}
// The pairs are now grouped. When there are only three group kinds for
// create/update/delete, conditionPairs has a length of 3. Each of the groups
// has then as many pairs as grouped together. Below these groups are limited.
var conditionPairs [][]CommonClusterStatusCondition
for len(conditionGroups) > 0 {
var p []CommonClusterStatusCondition
for _, g := range conditionGroups {
if len(p) == 0 {
p = append(p, g...)
conditionGroups = conditionGroups[1:]
continue
}
if len(g) >= 1 {
if isConditionPair(p[0], g[0]) || isConditionPair(p[1], g[0]) {
p = append(p, g...)
conditionGroups = conditionGroups[1:]
}
}
}
conditionPairs = append(conditionPairs, p)
}
// Here the list is finally flattened again and its pairs are limitted to the
// input parameter.
var limittedList []CommonClusterStatusCondition
for _, p := range conditionPairs {
// We compute the pair limit here for the total number of items. This is why
// we multiply by 2. When the limit is 5, we want to track for instance 5
// Updating/Updated pairs. Additionally when there is an item of a new pair
// and the list must be capped, the additional odd of the new item has to be
// considered when computing the limit. This results in an additional pair
// being dropped. Test case 6 demonstrates that.
l := (limit * 2) - (len(p) % 2)
if len(p) < l {
l = len(p)
}
limittedList = append(limittedList, p[len(p)-l:]...)
}
// We reverse the list order to have the item with the highest timestamp at
// the top again.
sort.Sort(sort.Reverse(sortClusterStatusConditionsByDate(limittedList)))
return limittedList
}
// withVersion computes a list of version history using the given list and new
// version structure to append. withVersion also limits the total amount of
// elements in the list by cutting off the tail with respect to the limit
// parameter.
func withVersion(versions []CommonClusterStatusVersion, version CommonClusterStatusVersion, limit int) []CommonClusterStatusVersion {
if hasVersion(versions, version.Version) {
return versions
}
// Create a copy to not manipulate the input list.
var newVersions []CommonClusterStatusVersion
newVersions = append(newVersions, versions...)
// Sort the versions in a way that the newest version, namely the one with the
// highest timestamp, is at the top of the list.
sort.Sort(sort.Reverse(sortClusterStatusVersionsByDate(newVersions)))
// Calculate the index for capping the list in the next step.
l := limit - 1
if len(newVersions) < l {
l = len(newVersions)
}
// Cap the list and prepend the new version.
newVersions = append([]CommonClusterStatusVersion{version}, newVersions[0:l]...)
return newVersions
}