@@ -20,8 +20,10 @@ import (
20
20
"fmt"
21
21
"net/url"
22
22
"strings"
23
+ "time"
23
24
24
- "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
25
+ apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
26
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25
27
)
26
28
27
29
// IsProtectedCommunityGroup returns whether or not a group specified for a CRD is protected for the community and needs
@@ -54,18 +56,205 @@ const (
54
56
55
57
// GetAPIApprovalState returns the state of the API approval and reason for that state
56
58
func GetAPIApprovalState (annotations map [string ]string ) (state APIApprovalState , reason string ) {
57
- annotation := annotations [v1beta1 .KubeAPIApprovedAnnotation ]
59
+ annotation := annotations [apiextensions .KubeAPIApprovedAnnotation ]
58
60
59
61
// we use the result of this parsing in the switch/case below
60
62
url , annotationURLParseErr := url .ParseRequestURI (annotation )
61
63
switch {
62
64
case len (annotation ) == 0 :
63
- return APIApprovalMissing , fmt .Sprintf ("protected groups must have approval annotation %q, see https://github.com/kubernetes/enhancements/pull/1111" , v1beta1 .KubeAPIApprovedAnnotation )
65
+ return APIApprovalMissing , fmt .Sprintf ("protected groups must have approval annotation %q, see https://github.com/kubernetes/enhancements/pull/1111" , apiextensions .KubeAPIApprovedAnnotation )
64
66
case strings .HasPrefix (annotation , "unapproved" ):
65
67
return APIApprovalBypassed , fmt .Sprintf ("not approved: %q" , annotation )
66
68
case annotationURLParseErr == nil && url != nil && len (url .Host ) > 0 && len (url .Scheme ) > 0 :
67
69
return APIApproved , fmt .Sprintf ("approved in %v" , annotation )
68
70
default :
69
- return APIApprovalInvalid , fmt .Sprintf ("protected groups must have approval annotation %q with either a URL or a reason starting with \" unapproved\" , see https://github.com/kubernetes/enhancements/pull/1111" , v1beta1 .KubeAPIApprovedAnnotation )
71
+ return APIApprovalInvalid , fmt .Sprintf ("protected groups must have approval annotation %q with either a URL or a reason starting with \" unapproved\" , see https://github.com/kubernetes/enhancements/pull/1111" , apiextensions .KubeAPIApprovedAnnotation )
70
72
}
71
73
}
74
+
75
+ // SetCRDCondition sets the status condition. It either overwrites the existing one or creates a new one.
76
+ func SetCRDCondition (crd * apiextensions.CustomResourceDefinition , newCondition apiextensions.CustomResourceDefinitionCondition ) {
77
+ newCondition .LastTransitionTime = metav1 .NewTime (time .Now ())
78
+
79
+ existingCondition := FindCRDCondition (crd , newCondition .Type )
80
+ if existingCondition == nil {
81
+ crd .Status .Conditions = append (crd .Status .Conditions , newCondition )
82
+ return
83
+ }
84
+
85
+ if existingCondition .Status != newCondition .Status || existingCondition .LastTransitionTime .IsZero () {
86
+ existingCondition .LastTransitionTime = newCondition .LastTransitionTime
87
+ }
88
+
89
+ existingCondition .Status = newCondition .Status
90
+ existingCondition .Reason = newCondition .Reason
91
+ existingCondition .Message = newCondition .Message
92
+ }
93
+
94
+ // RemoveCRDCondition removes the status condition.
95
+ func RemoveCRDCondition (crd * apiextensions.CustomResourceDefinition , conditionType apiextensions.CustomResourceDefinitionConditionType ) {
96
+ newConditions := []apiextensions.CustomResourceDefinitionCondition {}
97
+ for _ , condition := range crd .Status .Conditions {
98
+ if condition .Type != conditionType {
99
+ newConditions = append (newConditions , condition )
100
+ }
101
+ }
102
+ crd .Status .Conditions = newConditions
103
+ }
104
+
105
+ // FindCRDCondition returns the condition you're looking for or nil.
106
+ func FindCRDCondition (crd * apiextensions.CustomResourceDefinition , conditionType apiextensions.CustomResourceDefinitionConditionType ) * apiextensions.CustomResourceDefinitionCondition {
107
+ for i := range crd .Status .Conditions {
108
+ if crd .Status .Conditions [i ].Type == conditionType {
109
+ return & crd .Status .Conditions [i ]
110
+ }
111
+ }
112
+
113
+ return nil
114
+ }
115
+
116
+ // IsCRDConditionTrue indicates if the condition is present and strictly true.
117
+ func IsCRDConditionTrue (crd * apiextensions.CustomResourceDefinition , conditionType apiextensions.CustomResourceDefinitionConditionType ) bool {
118
+ return IsCRDConditionPresentAndEqual (crd , conditionType , apiextensions .ConditionTrue )
119
+ }
120
+
121
+ // IsCRDConditionFalse indicates if the condition is present and false.
122
+ func IsCRDConditionFalse (crd * apiextensions.CustomResourceDefinition , conditionType apiextensions.CustomResourceDefinitionConditionType ) bool {
123
+ return IsCRDConditionPresentAndEqual (crd , conditionType , apiextensions .ConditionFalse )
124
+ }
125
+
126
+ // IsCRDConditionPresentAndEqual indicates if the condition is present and equal to the given status.
127
+ func IsCRDConditionPresentAndEqual (crd * apiextensions.CustomResourceDefinition , conditionType apiextensions.CustomResourceDefinitionConditionType , status apiextensions.ConditionStatus ) bool {
128
+ for _ , condition := range crd .Status .Conditions {
129
+ if condition .Type == conditionType {
130
+ return condition .Status == status
131
+ }
132
+ }
133
+ return false
134
+ }
135
+
136
+ // IsCRDConditionEquivalent returns true if the lhs and rhs are equivalent except for times.
137
+ func IsCRDConditionEquivalent (lhs , rhs * apiextensions.CustomResourceDefinitionCondition ) bool {
138
+ if lhs == nil && rhs == nil {
139
+ return true
140
+ }
141
+ if lhs == nil || rhs == nil {
142
+ return false
143
+ }
144
+
145
+ return lhs .Message == rhs .Message && lhs .Reason == rhs .Reason && lhs .Status == rhs .Status && lhs .Type == rhs .Type
146
+ }
147
+
148
+ // CRDHasFinalizer returns true if the finalizer is in the list.
149
+ func CRDHasFinalizer (crd * apiextensions.CustomResourceDefinition , needle string ) bool {
150
+ for _ , finalizer := range crd .Finalizers {
151
+ if finalizer == needle {
152
+ return true
153
+ }
154
+ }
155
+
156
+ return false
157
+ }
158
+
159
+ // CRDRemoveFinalizer removes the finalizer if present.
160
+ func CRDRemoveFinalizer (crd * apiextensions.CustomResourceDefinition , needle string ) {
161
+ newFinalizers := []string {}
162
+ for _ , finalizer := range crd .Finalizers {
163
+ if finalizer != needle {
164
+ newFinalizers = append (newFinalizers , finalizer )
165
+ }
166
+ }
167
+ crd .Finalizers = newFinalizers
168
+ }
169
+
170
+ // HasServedCRDVersion returns true if the given version is in the list of CRD's versions and the Served flag is set.
171
+ func HasServedCRDVersion (crd * apiextensions.CustomResourceDefinition , version string ) bool {
172
+ for _ , v := range crd .Spec .Versions {
173
+ if v .Name == version {
174
+ return v .Served
175
+ }
176
+ }
177
+ return false
178
+ }
179
+
180
+ // GetCRDStorageVersion returns the storage version for given CRD.
181
+ func GetCRDStorageVersion (crd * apiextensions.CustomResourceDefinition ) (string , error ) {
182
+ for _ , v := range crd .Spec .Versions {
183
+ if v .Storage {
184
+ return v .Name , nil
185
+ }
186
+ }
187
+ // This should not happened if crd is valid
188
+ return "" , fmt .Errorf ("invalid apiextensions.CustomResourceDefinition, no storage version" )
189
+ }
190
+
191
+ // IsStoredVersion returns whether the given version is the storage version of the CRD.
192
+ func IsStoredVersion (crd * apiextensions.CustomResourceDefinition , version string ) bool {
193
+ for _ , v := range crd .Status .StoredVersions {
194
+ if version == v {
195
+ return true
196
+ }
197
+ }
198
+ return false
199
+ }
200
+
201
+ // GetSchemaForVersion returns the validation schema for the given version or nil.
202
+ func GetSchemaForVersion (crd * apiextensions.CustomResourceDefinition , version string ) (* apiextensions.CustomResourceValidation , error ) {
203
+ for _ , v := range crd .Spec .Versions {
204
+ if version == v .Name {
205
+ return v .Schema , nil
206
+ }
207
+ }
208
+ return nil , fmt .Errorf ("version %s not found in apiextensions.CustomResourceDefinition: %v" , version , crd .Name )
209
+ }
210
+
211
+ // GetSubresourcesForVersion returns the subresources for given version or nil.
212
+ func GetSubresourcesForVersion (crd * apiextensions.CustomResourceDefinition , version string ) (* apiextensions.CustomResourceSubresources , error ) {
213
+ for _ , v := range crd .Spec .Versions {
214
+ if version == v .Name {
215
+ return v .Subresources , nil
216
+ }
217
+ }
218
+ return nil , fmt .Errorf ("version %s not found in apiextensions.CustomResourceDefinition: %v" , version , crd .Name )
219
+ }
220
+
221
+ // HasPerVersionSchema returns true if a CRD uses per-version schema.
222
+ func HasPerVersionSchema (versions []apiextensions.CustomResourceDefinitionVersion ) bool {
223
+ for _ , v := range versions {
224
+ if v .Schema != nil {
225
+ return true
226
+ }
227
+ }
228
+ return false
229
+ }
230
+
231
+ // HasPerVersionSubresources returns true if a CRD uses per-version subresources.
232
+ func HasPerVersionSubresources (versions []apiextensions.CustomResourceDefinitionVersion ) bool {
233
+ for _ , v := range versions {
234
+ if v .Subresources != nil {
235
+ return true
236
+ }
237
+ }
238
+ return false
239
+ }
240
+
241
+ // HasPerVersionColumns returns true if a CRD uses per-version columns.
242
+ func HasPerVersionColumns (versions []apiextensions.CustomResourceDefinitionVersion ) bool {
243
+ for _ , v := range versions {
244
+ if len (v .AdditionalPrinterColumns ) > 0 {
245
+ return true
246
+ }
247
+ }
248
+ return false
249
+ }
250
+
251
+ // HasVersionServed returns true if given CRD has given version served.
252
+ func HasVersionServed (crd * apiextensions.CustomResourceDefinition , version string ) bool {
253
+ for _ , v := range crd .Spec .Versions {
254
+ if ! v .Served || v .Name != version {
255
+ continue
256
+ }
257
+ return true
258
+ }
259
+ return false
260
+ }
0 commit comments