Skip to content

Commit 2bb06e4

Browse files
committed
Add ConditionsFromStatus and SetApplyConfigurationStatusCondition helpers
Introduces SSA-friendly helpers to convert []metav1.Condition into a slice of ConditionApplyConfiguration and to set a condition by type while mirroring meta.SetStatusCondition's LastTransitionTime semantics. Foundation commit for migrating controllers to server-side apply; no controller currently uses them. Extracted from 0b757a84915f4ccdfa11c6dbd63eb10a3bfba582.
1 parent 8854ff6 commit 2bb06e4

2 files changed

Lines changed: 424 additions & 0 deletions

File tree

internal/utils/conditions.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
SPDX-FileCopyrightText: Copyright 2024 SAP SE or an SAP affiliate company and cobaltcore-dev contributors
3+
SPDX-License-Identifier: Apache-2.0
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
*/
17+
18+
package utils
19+
20+
import (
21+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22+
k8sacmetav1 "k8s.io/client-go/applyconfigurations/meta/v1"
23+
)
24+
25+
// ConditionsFromStatus converts a []metav1.Condition (as stored in a status
26+
// subresource) into a []k8sacmetav1.ConditionApplyConfiguration suitable for
27+
// use with server-side apply. All fields including LastTransitionTime are
28+
// preserved verbatim.
29+
func ConditionsFromStatus(conditions []metav1.Condition) []k8sacmetav1.ConditionApplyConfiguration {
30+
result := make([]k8sacmetav1.ConditionApplyConfiguration, len(conditions))
31+
for i := range conditions {
32+
c := &conditions[i]
33+
result[i] = k8sacmetav1.ConditionApplyConfiguration{
34+
Type: &c.Type,
35+
Status: &c.Status,
36+
Reason: &c.Reason,
37+
Message: &c.Message,
38+
LastTransitionTime: &c.LastTransitionTime,
39+
}
40+
}
41+
return result
42+
}
43+
44+
// SetApplyConfigurationStatusCondition sets the corresponding condition in
45+
// conditions to newCondition and returns true if the conditions are changed by
46+
// this call. It mirrors the behaviour of k8s.io/apimachinery/pkg/api/meta.SetStatusCondition:
47+
//
48+
// 1. If a condition of the specified type does not exist, newCondition is
49+
// appended. LastTransitionTime is set to now if not provided.
50+
// 2. If a condition of the specified type already exists, all fields are
51+
// updated. LastTransitionTime is updated to now (or the provided value)
52+
// only when Status changes; otherwise the existing LastTransitionTime is
53+
// preserved.
54+
func SetApplyConfigurationStatusCondition(conditions *[]k8sacmetav1.ConditionApplyConfiguration, newCondition k8sacmetav1.ConditionApplyConfiguration) (changed bool) {
55+
if conditions == nil {
56+
return false
57+
}
58+
59+
// Find existing entry by type
60+
var existing *k8sacmetav1.ConditionApplyConfiguration
61+
for i := range *conditions {
62+
if (*conditions)[i].Type != nil && *(*conditions)[i].Type == *newCondition.Type {
63+
existing = &(*conditions)[i]
64+
break
65+
}
66+
}
67+
68+
if existing == nil {
69+
// New condition: set LastTransitionTime if not provided
70+
if newCondition.LastTransitionTime == nil {
71+
now := metav1.Now()
72+
newCondition.LastTransitionTime = &now
73+
}
74+
*conditions = append(*conditions, newCondition)
75+
return true
76+
}
77+
78+
// Existing condition: update fields, handle LastTransitionTime
79+
statusChanged := existing.Status == nil || newCondition.Status == nil ||
80+
*existing.Status != *newCondition.Status
81+
82+
if statusChanged {
83+
existing.Status = newCondition.Status
84+
if newCondition.LastTransitionTime != nil {
85+
existing.LastTransitionTime = newCondition.LastTransitionTime
86+
} else {
87+
now := metav1.Now()
88+
existing.LastTransitionTime = &now
89+
}
90+
changed = true
91+
}
92+
// When status is unchanged, LastTransitionTime is intentionally preserved.
93+
94+
if strChanged(&existing.Reason, newCondition.Reason) {
95+
existing.Reason = newCondition.Reason
96+
changed = true
97+
}
98+
if strChanged(&existing.Message, newCondition.Message) {
99+
existing.Message = newCondition.Message
100+
changed = true
101+
}
102+
103+
return changed
104+
}
105+
106+
// strChanged reports whether the pointer value of b differs from the current
107+
// value at a.
108+
func strChanged(a **string, b *string) bool {
109+
if *a == nil && b == nil {
110+
return false
111+
}
112+
if *a == nil || b == nil {
113+
return true
114+
}
115+
return **a != *b
116+
}

0 commit comments

Comments
 (0)