Skip to content

Commit 542a007

Browse files
committed
add helpers for managing conditions
1 parent c53ce48 commit 542a007

File tree

3 files changed

+321
-0
lines changed

3 files changed

+321
-0
lines changed

staging/src/k8s.io/apimachinery/pkg/api/meta/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ load(
99
go_test(
1010
name = "go_default_test",
1111
srcs = [
12+
"conditions_test.go",
1213
"meta_test.go",
1314
"multirestmapper_test.go",
1415
"priority_test.go",
@@ -27,6 +28,7 @@ go_test(
2728
go_library(
2829
name = "go_default_library",
2930
srcs = [
31+
"conditions.go",
3032
"doc.go",
3133
"errors.go",
3234
"firsthit_restmapper.go",
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package meta
18+
19+
import (
20+
"time"
21+
22+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23+
)
24+
25+
// SetStatusCondition sets the corresponding condition in conditions to newCondition.
26+
// conditions must be non-nil.
27+
// 1. if the condition of the specified type already exists (all fields of the existing condition are updated to
28+
// newCondition, LastTransitionTime is set to now if the new status differs from the old status)
29+
// 2. if a condition of the specified type does not exist (LastTransitionTime is set to now() if unset, and newCondition is appended)
30+
func SetStatusCondition(conditions *[]metav1.Condition, newCondition metav1.Condition) {
31+
if conditions == nil {
32+
return
33+
}
34+
existingCondition := FindStatusCondition(*conditions, newCondition.Type)
35+
if existingCondition == nil {
36+
if newCondition.LastTransitionTime.IsZero() {
37+
newCondition.LastTransitionTime = metav1.NewTime(time.Now())
38+
}
39+
*conditions = append(*conditions, newCondition)
40+
return
41+
}
42+
43+
if existingCondition.Status != newCondition.Status {
44+
existingCondition.Status = newCondition.Status
45+
if !newCondition.LastTransitionTime.IsZero() {
46+
existingCondition.LastTransitionTime = newCondition.LastTransitionTime
47+
} else {
48+
existingCondition.LastTransitionTime = metav1.NewTime(time.Now())
49+
}
50+
}
51+
52+
existingCondition.Reason = newCondition.Reason
53+
existingCondition.Message = newCondition.Message
54+
}
55+
56+
// RemoveStatusCondition removes the corresponding conditionType from conditions.
57+
// conditions must be non-nil.
58+
func RemoveStatusCondition(conditions *[]metav1.Condition, conditionType string) {
59+
if conditions == nil {
60+
return
61+
}
62+
newConditions := make([]metav1.Condition, 0, len(*conditions)-1)
63+
for _, condition := range *conditions {
64+
if condition.Type != conditionType {
65+
newConditions = append(newConditions, condition)
66+
}
67+
}
68+
69+
*conditions = newConditions
70+
}
71+
72+
// FindStatusCondition finds the conditionType in conditions.
73+
func FindStatusCondition(conditions []metav1.Condition, conditionType string) *metav1.Condition {
74+
for i := range conditions {
75+
if conditions[i].Type == conditionType {
76+
return &conditions[i]
77+
}
78+
}
79+
80+
return nil
81+
}
82+
83+
// IsStatusConditionTrue returns true when the conditionType is present and set to `metav1.ConditionTrue`
84+
func IsStatusConditionTrue(conditions []metav1.Condition, conditionType string) bool {
85+
return IsStatusConditionPresentAndEqual(conditions, conditionType, metav1.ConditionTrue)
86+
}
87+
88+
// IsStatusConditionFalse returns true when the conditionType is present and set to `metav1.ConditionFalse`
89+
func IsStatusConditionFalse(conditions []metav1.Condition, conditionType string) bool {
90+
return IsStatusConditionPresentAndEqual(conditions, conditionType, metav1.ConditionFalse)
91+
}
92+
93+
// IsStatusConditionPresentAndEqual returns true when conditionType is present and equal to status.
94+
func IsStatusConditionPresentAndEqual(conditions []metav1.Condition, conditionType string, status metav1.ConditionStatus) bool {
95+
for _, condition := range conditions {
96+
if condition.Type == conditionType {
97+
return condition.Status == status
98+
}
99+
}
100+
return false
101+
}
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package meta
18+
19+
import (
20+
"reflect"
21+
"testing"
22+
"time"
23+
24+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25+
)
26+
27+
func TestSetStatusCondition(t *testing.T) {
28+
oneHourBefore := time.Now().Add(-1 * time.Hour)
29+
oneHourAfter := time.Now().Add(1 * time.Hour)
30+
31+
tests := []struct {
32+
name string
33+
conditions []metav1.Condition
34+
toAdd metav1.Condition
35+
expected []metav1.Condition
36+
}{
37+
{
38+
name: "should-add",
39+
conditions: []metav1.Condition{
40+
{Type: "first"},
41+
{Type: "third"},
42+
},
43+
toAdd: metav1.Condition{Type: "second", Status: metav1.ConditionTrue, LastTransitionTime: metav1.Time{Time: oneHourBefore}, Reason: "reason", Message: "message"},
44+
expected: []metav1.Condition{
45+
{Type: "first"},
46+
{Type: "third"},
47+
{Type: "second", Status: metav1.ConditionTrue, LastTransitionTime: metav1.Time{Time: oneHourBefore}, Reason: "reason", Message: "message"},
48+
},
49+
},
50+
{
51+
name: "use-supplied-time",
52+
conditions: []metav1.Condition{
53+
{Type: "first"},
54+
{Type: "second", Status: metav1.ConditionFalse},
55+
{Type: "third"},
56+
},
57+
toAdd: metav1.Condition{Type: "second", Status: metav1.ConditionTrue, LastTransitionTime: metav1.Time{Time: oneHourBefore}, Reason: "reason", Message: "message"},
58+
expected: []metav1.Condition{
59+
{Type: "first"},
60+
{Type: "second", Status: metav1.ConditionTrue, LastTransitionTime: metav1.Time{Time: oneHourBefore}, Reason: "reason", Message: "message"},
61+
{Type: "third"},
62+
},
63+
},
64+
{
65+
name: "update-fields",
66+
conditions: []metav1.Condition{
67+
{Type: "first"},
68+
{Type: "second", Status: metav1.ConditionTrue, LastTransitionTime: metav1.Time{Time: oneHourBefore}},
69+
{Type: "third"},
70+
},
71+
toAdd: metav1.Condition{Type: "second", Status: metav1.ConditionTrue, LastTransitionTime: metav1.Time{Time: oneHourAfter}, Reason: "reason", Message: "message"},
72+
expected: []metav1.Condition{
73+
{Type: "first"},
74+
{Type: "second", Status: metav1.ConditionTrue, LastTransitionTime: metav1.Time{Time: oneHourBefore}, Reason: "reason", Message: "message"},
75+
{Type: "third"},
76+
},
77+
},
78+
}
79+
80+
for _, test := range tests {
81+
t.Run(test.name, func(t *testing.T) {
82+
SetStatusCondition(&test.conditions, test.toAdd)
83+
if !reflect.DeepEqual(test.conditions, test.expected) {
84+
t.Error(test.conditions)
85+
}
86+
})
87+
}
88+
}
89+
90+
func TestRemoveStatusCondition(t *testing.T) {
91+
tests := []struct {
92+
name string
93+
conditions []metav1.Condition
94+
conditionType string
95+
expected []metav1.Condition
96+
}{
97+
{
98+
name: "present",
99+
conditions: []metav1.Condition{
100+
{Type: "first"},
101+
{Type: "second"},
102+
{Type: "third"},
103+
},
104+
conditionType: "second",
105+
expected: []metav1.Condition{
106+
{Type: "first"},
107+
{Type: "third"},
108+
},
109+
},
110+
{
111+
name: "not-present",
112+
conditions: []metav1.Condition{
113+
{Type: "first"},
114+
{Type: "second"},
115+
{Type: "third"},
116+
},
117+
conditionType: "fourth",
118+
expected: []metav1.Condition{
119+
{Type: "first"},
120+
{Type: "second"},
121+
{Type: "third"},
122+
},
123+
},
124+
}
125+
126+
for _, test := range tests {
127+
t.Run(test.name, func(t *testing.T) {
128+
RemoveStatusCondition(&test.conditions, test.conditionType)
129+
if !reflect.DeepEqual(test.conditions, test.expected) {
130+
t.Error(test.conditions)
131+
}
132+
})
133+
}
134+
}
135+
136+
func TestFindStatusCondition(t *testing.T) {
137+
tests := []struct {
138+
name string
139+
conditions []metav1.Condition
140+
conditionType string
141+
expected *metav1.Condition
142+
}{
143+
{
144+
name: "not-present",
145+
conditions: []metav1.Condition{
146+
{Type: "first"},
147+
},
148+
conditionType: "second",
149+
expected: nil,
150+
},
151+
{
152+
name: "present",
153+
conditions: []metav1.Condition{
154+
{Type: "first"},
155+
{Type: "second"},
156+
},
157+
conditionType: "second",
158+
expected: &metav1.Condition{Type: "second"},
159+
},
160+
}
161+
162+
for _, test := range tests {
163+
t.Run(test.name, func(t *testing.T) {
164+
actual := FindStatusCondition(test.conditions, test.conditionType)
165+
if !reflect.DeepEqual(actual, test.expected) {
166+
t.Error(actual)
167+
}
168+
})
169+
}
170+
}
171+
172+
func TestIsStatusConditionPresentAndEqual(t *testing.T) {
173+
tests := []struct {
174+
name string
175+
conditions []metav1.Condition
176+
conditionType string
177+
conditionStatus metav1.ConditionStatus
178+
expected bool
179+
}{
180+
{
181+
name: "doesnt-match-true",
182+
conditions: []metav1.Condition{
183+
{Type: "first", Status: metav1.ConditionUnknown},
184+
},
185+
conditionType: "first",
186+
conditionStatus: metav1.ConditionTrue,
187+
expected: false,
188+
},
189+
{
190+
name: "does-match-true",
191+
conditions: []metav1.Condition{
192+
{Type: "first", Status: metav1.ConditionTrue},
193+
},
194+
conditionType: "first",
195+
conditionStatus: metav1.ConditionTrue,
196+
expected: true,
197+
},
198+
{
199+
name: "does-match-false",
200+
conditions: []metav1.Condition{
201+
{Type: "first", Status: metav1.ConditionFalse},
202+
},
203+
conditionType: "first",
204+
conditionStatus: metav1.ConditionFalse,
205+
expected: true,
206+
},
207+
}
208+
209+
for _, test := range tests {
210+
t.Run(test.name, func(t *testing.T) {
211+
actual := IsStatusConditionPresentAndEqual(test.conditions, test.conditionType, test.conditionStatus)
212+
if actual != test.expected {
213+
t.Error(actual)
214+
}
215+
216+
})
217+
}
218+
}

0 commit comments

Comments
 (0)