@@ -24,8 +24,6 @@ import (
24
24
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25
25
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
26
26
"k8s.io/apimachinery/pkg/runtime"
27
-
28
- clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
29
27
)
30
28
31
29
// TODO: Move to the API package.
@@ -34,99 +32,88 @@ const (
34
32
NoReasonReported = "NoReasonReported"
35
33
)
36
34
37
- // MustGet is like Get except that it panics on error.
38
- func MustGet (sourceObj runtime.Object , sourceConditionType string ) * metav1.Condition {
39
- v , err := Get (sourceObj , sourceConditionType )
40
- if err != nil {
41
- panic (err )
42
- }
43
- return v
35
+ // Getter interface defines methods that an API object should implement in order to
36
+ // use the conditions package for getting conditions.
37
+ type Getter interface {
38
+ // GetV1Beta2Conditions returns the list of conditions for a cluster API object.
39
+ // Note: GetV1Beta2Conditions will be renamed to GetConditions in a later stage of the transition to V1Beta2.
40
+ GetV1Beta2Conditions () []metav1.Condition
44
41
}
45
42
46
- // Get returns a condition from the sourceObj .
43
+ // Get returns a condition from the object implementing the Getter interface .
47
44
//
48
- // Get supports retrieving conditions from objects at different stages of the transition to the metav1.Condition type:
49
- // - Objects with clusterv1.Conditions in status.conditions; in this case a best effort conversion
50
- // to metav1.Condition is performed, just enough to allow surfacing a condition from a provider object with Mirror
51
- // - Objects with metav1.Condition in status.v1beta2.conditions
52
- // - Objects with metav1.Condition in status.conditions
53
- //
54
- // Please note that Get also supports reading conditions from unstructured objects; in this case, best effort
55
- // conversion from a map is performed, just enough to allow surfacing a condition from a providers object with Mirror
56
- //
57
- // In case the object does not have metav1.Conditions, Get tries to read clusterv1.Conditions from status.conditions
58
- // and convert them to metav1.Conditions.
59
- func Get (sourceObj runtime.Object , sourceConditionType string ) (* metav1.Condition , error ) {
60
- conditions , err := GetAll (sourceObj )
61
- if err != nil {
62
- return nil , err
45
+ // Please note that Get does not support reading conditions from unstructured objects nor from API types not implementing
46
+ // the Getter interface. Eventually, users can implement wrappers on those types implementing this interface and
47
+ // taking care of aligning the condition format if necessary.
48
+ func Get (sourceObj Getter , sourceConditionType string ) * metav1.Condition {
49
+ // if obj is nil, the requested condition does not exist.
50
+ if sourceObj == nil {
51
+ return nil
63
52
}
64
- return meta .FindStatusCondition (conditions , sourceConditionType ), nil
53
+
54
+ // if obj is a zero value of its type (a pointer to nil), the requested condition does not exist.
55
+ ptr := reflect .ValueOf (sourceObj )
56
+ if ptr .Kind () != reflect .Pointer {
57
+ // Note: this should never happen, given that Getter is an interface.
58
+ return nil
59
+ }
60
+
61
+ elem := ptr .Elem ()
62
+ if ! elem .IsValid () {
63
+ return nil
64
+ }
65
+
66
+ // Otherwise get the requested condition.
67
+ return meta .FindStatusCondition (sourceObj .GetV1Beta2Conditions (), sourceConditionType )
65
68
}
66
69
67
- // GetAll returns all the conditions from the object.
70
+ // UnstructuredGet returns a condition from an Unstructured object.
68
71
//
69
- // GetAll supports retrieving conditions from objects at different stages of the transition to the metav1.Condition type:
72
+ // UnstructuredGet supports retrieving conditions from objects at different stages of the transition from
73
+ // clusterv1.conditions to the metav1.Condition type:
70
74
// - Objects with clusterv1.Conditions in status.conditions; in this case a best effort conversion
71
- // to metav1.Condition is performed, just enough to allow surfacing a condition from a providers object with Mirror
75
+ // to metav1.Condition is performed, just enough to allow surfacing a condition from a provider object with Mirror
72
76
// - Objects with metav1.Condition in status.v1beta2.conditions
73
77
// - Objects with metav1.Condition in status.conditions
74
- //
75
- // Please note that GetAll also supports reading conditions from unstructured objects; in this case, best effort
76
- // conversion from a map is performed, just enough to allow surfacing a condition from a providers object with Mirror
77
- //
78
- // In case the object does not have metav1.Conditions, GetAll tries to read clusterv1.Conditions from status.conditions
79
- // and convert them to metav1.Conditions.
80
- func GetAll (sourceObj runtime.Object ) ([]metav1.Condition , error ) {
78
+ func UnstructuredGet (sourceObj runtime.Unstructured , sourceConditionType string ) (* metav1.Condition , error ) {
79
+ // if obj is nil, the requested condition does not exist.
81
80
if sourceObj == nil {
82
- return nil , errors .New ("sourceObj cannot be nil" )
83
- }
84
-
85
- switch sourceObj .(type ) {
86
- case runtime.Unstructured :
87
- return getFromUnstructuredObject (sourceObj )
88
- default :
89
- return getFromTypedObject (sourceObj )
90
- }
91
- }
92
-
93
- func getFromUnstructuredObject (obj runtime.Object ) ([]metav1.Condition , error ) {
94
- u , ok := obj .(runtime.Unstructured )
95
- if ! ok {
96
- // NOTE: this should not happen due to the type assertion before calling this func
97
- return nil , errors .New ("obj cannot be converted to runtime.Unstructured" )
81
+ return nil , nil
98
82
}
99
83
100
- if ! reflect .ValueOf (u ).Elem ().IsValid () {
101
- return nil , errors .New ("obj cannot be nil" )
84
+ // if obj is a zero value of its type (a pointer to nil), the requested condition does not exist.
85
+ ptr := reflect .ValueOf (sourceObj )
86
+ if ptr .Kind () != reflect .Pointer {
87
+ // Note: this should never happen, given that Getter is an interface.
88
+ return nil , nil
102
89
}
103
90
104
- ownerInfo := getConditionOwnerInfo (obj )
91
+ ownerInfo := getConditionOwnerInfo (sourceObj )
105
92
106
- value , exists , err := unstructured .NestedFieldNoCopy (u .UnstructuredContent (), "status" , "v1beta2" , "conditions" )
93
+ value , exists , err := unstructured .NestedFieldNoCopy (sourceObj .UnstructuredContent (), "status" , "v1beta2" , "conditions" )
107
94
if exists && err == nil {
108
95
if conditions , ok := value .([]interface {}); ok {
109
96
r , err := convertFromUnstructuredConditions (conditions )
110
97
if err != nil {
111
98
return nil , errors .Wrapf (err , "failed to convert %s.status.v1beta2.conditions to []metav1.Condition" , ownerInfo )
112
99
}
113
- return r , nil
100
+ return meta . FindStatusCondition ( r , sourceConditionType ) , nil
114
101
}
115
102
}
116
103
117
- value , exists , err = unstructured .NestedFieldNoCopy (u .UnstructuredContent (), "status" , "conditions" )
104
+ value , exists , err = unstructured .NestedFieldNoCopy (sourceObj .UnstructuredContent (), "status" , "conditions" )
118
105
if exists && err == nil {
119
106
if conditions , ok := value .([]interface {}); ok {
120
107
r , err := convertFromUnstructuredConditions (conditions )
121
108
if err != nil {
122
109
return nil , errors .Wrapf (err , "failed to convert %s.status.conditions to []metav1.Condition" , ownerInfo )
123
110
}
124
- return r , nil
111
+ return meta . FindStatusCondition ( r , sourceConditionType ) , nil
125
112
}
126
113
}
127
114
128
115
// With unstructured, it is not possible to detect if conditions are not set if the type is wrongly defined.
129
- // We assume condition are not set.
116
+ // This methods assume condition are not set.
130
117
return nil , nil
131
118
}
132
119
@@ -194,93 +181,6 @@ func convertFromUnstructuredConditions(conditions []interface{}) ([]metav1.Condi
194
181
return convertedConditions , nil
195
182
}
196
183
197
- func getFromTypedObject (obj runtime.Object ) ([]metav1.Condition , error ) {
198
- ptr := reflect .ValueOf (obj )
199
- if ptr .Kind () != reflect .Pointer {
200
- return nil , errors .New ("obj must be a pointer" )
201
- }
202
-
203
- elem := ptr .Elem ()
204
- if ! elem .IsValid () {
205
- return nil , errors .New ("obj must be a valid value (non zero value of its type)" )
206
- }
207
-
208
- ownerInfo := getConditionOwnerInfo (obj )
209
-
210
- statusField := elem .FieldByName ("Status" )
211
- if statusField == (reflect.Value {}) {
212
- return nil , errors .Errorf ("%s must have a status field" , ownerInfo )
213
- }
214
-
215
- // Get conditions.
216
- // NOTE: Given that we allow providers to migrate at different speed, it is required to support objects at the different stage of the transition from legacy conditions to metav1.Condition.
217
- // In order to handle this, first try to read Status.V1Beta2.Conditions, then Status.Conditions; for Status.Conditions, also support conversion from legacy conditions.
218
- // The V1Beta2 branch and the conversion from legacy conditions should be dropped when the v1beta1 API is removed.
219
-
220
- if v1beta2Field := statusField .FieldByName ("V1Beta2" ); v1beta2Field != (reflect.Value {}) {
221
- if v1beta2Field .Kind () != reflect .Pointer {
222
- return nil , errors .Errorf ("%s.status.v1beta2 must be a pointer" , ownerInfo )
223
- }
224
-
225
- v1beta2Elem := v1beta2Field .Elem ()
226
- if ! v1beta2Elem .IsValid () {
227
- // If the field is a zero value of its type, condition list is nil.
228
- return nil , nil
229
- }
230
-
231
- if conditionField := v1beta2Elem .FieldByName ("Conditions" ); conditionField != (reflect.Value {}) {
232
- v1beta2Conditions , ok := conditionField .Interface ().([]metav1.Condition )
233
- if ! ok {
234
- return nil , errors .Errorf ("%s.status.v1beta2.conditions must be of type []metav1.Condition" , ownerInfo )
235
- }
236
- return v1beta2Conditions , nil
237
- }
238
- }
239
-
240
- if conditionField := statusField .FieldByName ("Conditions" ); conditionField != (reflect.Value {}) {
241
- conditions , ok := conditionField .Interface ().([]metav1.Condition )
242
- if ok {
243
- return conditions , nil
244
- }
245
-
246
- v1betaConditions , ok := conditionField .Interface ().(clusterv1.Conditions )
247
- if ! ok {
248
- return nil , errors .Errorf ("%s.status.conditions must be of type []metav1.Condition or clusterv1.Conditions" , ownerInfo )
249
- }
250
- r , err := convertFromV1Beta1Conditions (v1betaConditions )
251
- if err != nil {
252
- return nil , errors .Wrapf (err , "failed to convert %s.status.conditions to []metav1.Condition" , ownerInfo )
253
- }
254
- return r , nil
255
- }
256
-
257
- return nil , errors .Errorf ("%s.status must have one of conditions or v1beta2.conditions" , ownerInfo )
258
- }
259
-
260
- // convertFromV1Beta1Conditions converts a clusterv1.Conditions to []metav1.Condition.
261
- // NOTE: this operation is performed at best effort and assuming conditions have been set using Cluster API condition utils.
262
- func convertFromV1Beta1Conditions (v1betaConditions clusterv1.Conditions ) ([]metav1.Condition , error ) {
263
- if v1betaConditions == nil {
264
- return nil , nil
265
- }
266
-
267
- convertedConditions := make ([]metav1.Condition , len (v1betaConditions ))
268
- for i , c := range v1betaConditions {
269
- convertedConditions [i ] = metav1.Condition {
270
- Type : string (c .Type ),
271
- Status : metav1 .ConditionStatus (c .Status ),
272
- LastTransitionTime : c .LastTransitionTime ,
273
- Reason : c .Reason ,
274
- Message : c .Message ,
275
- }
276
-
277
- if err := validateAndFixConvertedCondition (& convertedConditions [i ]); err != nil {
278
- return nil , err
279
- }
280
- }
281
- return convertedConditions , nil
282
- }
283
-
284
184
// validateAndFixConvertedCondition validates and fixes a clusterv1.Condition converted to a metav1.Condition.
285
185
// this operation assumes conditions have been set using Cluster API condition utils;
286
186
// also, only a few, minimal rules are enforced, just enough to allow surfacing a condition from a providers object with Mirror.
0 commit comments