1
16
17 package apiextensions
18
19 import (
20 "fmt"
21 "time"
22
23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24 )
25
26 var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc()
27
28
29 func SetCRDCondition(crd *CustomResourceDefinition, newCondition CustomResourceDefinitionCondition) {
30 newCondition.LastTransitionTime = metav1.NewTime(time.Now())
31
32 existingCondition := FindCRDCondition(crd, newCondition.Type)
33 if existingCondition == nil {
34 crd.Status.Conditions = append(crd.Status.Conditions, newCondition)
35 return
36 }
37
38 if existingCondition.Status != newCondition.Status || existingCondition.LastTransitionTime.IsZero() {
39 existingCondition.LastTransitionTime = newCondition.LastTransitionTime
40 }
41
42 existingCondition.Status = newCondition.Status
43 existingCondition.Reason = newCondition.Reason
44 existingCondition.Message = newCondition.Message
45 }
46
47
48 func RemoveCRDCondition(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) {
49 newConditions := []CustomResourceDefinitionCondition{}
50 for _, condition := range crd.Status.Conditions {
51 if condition.Type != conditionType {
52 newConditions = append(newConditions, condition)
53 }
54 }
55 crd.Status.Conditions = newConditions
56 }
57
58
59 func FindCRDCondition(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) *CustomResourceDefinitionCondition {
60 for i := range crd.Status.Conditions {
61 if crd.Status.Conditions[i].Type == conditionType {
62 return &crd.Status.Conditions[i]
63 }
64 }
65
66 return nil
67 }
68
69
70 func IsCRDConditionTrue(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) bool {
71 return IsCRDConditionPresentAndEqual(crd, conditionType, ConditionTrue)
72 }
73
74
75 func IsCRDConditionFalse(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType) bool {
76 return IsCRDConditionPresentAndEqual(crd, conditionType, ConditionFalse)
77 }
78
79
80 func IsCRDConditionPresentAndEqual(crd *CustomResourceDefinition, conditionType CustomResourceDefinitionConditionType, status ConditionStatus) bool {
81 for _, condition := range crd.Status.Conditions {
82 if condition.Type == conditionType {
83 return condition.Status == status
84 }
85 }
86 return false
87 }
88
89
90 func IsCRDConditionEquivalent(lhs, rhs *CustomResourceDefinitionCondition) bool {
91 if lhs == nil && rhs == nil {
92 return true
93 }
94 if lhs == nil || rhs == nil {
95 return false
96 }
97
98 return lhs.Message == rhs.Message && lhs.Reason == rhs.Reason && lhs.Status == rhs.Status && lhs.Type == rhs.Type
99 }
100
101
102 func CRDHasFinalizer(crd *CustomResourceDefinition, needle string) bool {
103 for _, finalizer := range crd.Finalizers {
104 if finalizer == needle {
105 return true
106 }
107 }
108
109 return false
110 }
111
112
113 func CRDRemoveFinalizer(crd *CustomResourceDefinition, needle string) {
114 newFinalizers := []string{}
115 for _, finalizer := range crd.Finalizers {
116 if finalizer != needle {
117 newFinalizers = append(newFinalizers, finalizer)
118 }
119 }
120 crd.Finalizers = newFinalizers
121 }
122
123
124 func HasServedCRDVersion(crd *CustomResourceDefinition, version string) bool {
125 for _, v := range crd.Spec.Versions {
126 if v.Name == version {
127 return v.Served
128 }
129 }
130 return false
131 }
132
133
134 func GetCRDStorageVersion(crd *CustomResourceDefinition) (string, error) {
135 for _, v := range crd.Spec.Versions {
136 if v.Storage {
137 return v.Name, nil
138 }
139 }
140
141 return "", fmt.Errorf("invalid CustomResourceDefinition, no storage version")
142 }
143
144
145 func IsStoredVersion(crd *CustomResourceDefinition, version string) bool {
146 for _, v := range crd.Status.StoredVersions {
147 if version == v {
148 return true
149 }
150 }
151 return false
152 }
153
154
155 func GetSchemaForVersion(crd *CustomResourceDefinition, version string) (*CustomResourceValidation, error) {
156 if !HasPerVersionSchema(crd.Spec.Versions) {
157 return crd.Spec.Validation, nil
158 }
159 if crd.Spec.Validation != nil {
160 return nil, fmt.Errorf("malformed CustomResourceDefinition %s version %s: top-level and per-version schemas must be mutual exclusive", crd.Name, version)
161 }
162 for _, v := range crd.Spec.Versions {
163 if version == v.Name {
164 return v.Schema, nil
165 }
166 }
167 return nil, fmt.Errorf("version %s not found in CustomResourceDefinition: %v", version, crd.Name)
168 }
169
170
171 func GetSubresourcesForVersion(crd *CustomResourceDefinition, version string) (*CustomResourceSubresources, error) {
172 if !HasPerVersionSubresources(crd.Spec.Versions) {
173 return crd.Spec.Subresources, nil
174 }
175 if crd.Spec.Subresources != nil {
176 return nil, fmt.Errorf("malformed CustomResourceDefinition %s version %s: top-level and per-version subresources must be mutual exclusive", crd.Name, version)
177 }
178 for _, v := range crd.Spec.Versions {
179 if version == v.Name {
180 return v.Subresources, nil
181 }
182 }
183 return nil, fmt.Errorf("version %s not found in CustomResourceDefinition: %v", version, crd.Name)
184 }
185
186
187
188
189
190 func GetColumnsForVersion(crd *CustomResourceDefinition, version string) ([]CustomResourceColumnDefinition, error) {
191 if !HasPerVersionColumns(crd.Spec.Versions) {
192 return serveDefaultColumnsIfEmpty(crd.Spec.AdditionalPrinterColumns), nil
193 }
194 if len(crd.Spec.AdditionalPrinterColumns) > 0 {
195 return nil, fmt.Errorf("malformed CustomResourceDefinition %s version %s: top-level and per-version additionalPrinterColumns must be mutual exclusive", crd.Name, version)
196 }
197 for _, v := range crd.Spec.Versions {
198 if version == v.Name {
199 return serveDefaultColumnsIfEmpty(v.AdditionalPrinterColumns), nil
200 }
201 }
202 return nil, fmt.Errorf("version %s not found in CustomResourceDefinition: %v", version, crd.Name)
203 }
204
205
206 func HasPerVersionSchema(versions []CustomResourceDefinitionVersion) bool {
207 for _, v := range versions {
208 if v.Schema != nil {
209 return true
210 }
211 }
212 return false
213 }
214
215
216 func HasPerVersionSubresources(versions []CustomResourceDefinitionVersion) bool {
217 for _, v := range versions {
218 if v.Subresources != nil {
219 return true
220 }
221 }
222 return false
223 }
224
225
226 func HasPerVersionColumns(versions []CustomResourceDefinitionVersion) bool {
227 for _, v := range versions {
228 if len(v.AdditionalPrinterColumns) > 0 {
229 return true
230 }
231 }
232 return false
233 }
234
235
236
237
238
239 func serveDefaultColumnsIfEmpty(columns []CustomResourceColumnDefinition) []CustomResourceColumnDefinition {
240 if len(columns) > 0 {
241 return columns
242 }
243 return []CustomResourceColumnDefinition{
244 {Name: "Age", Type: "date", Description: swaggerMetadataDescriptions["creationTimestamp"], JSONPath: ".metadata.creationTimestamp"},
245 }
246 }
247
248
249 func HasVersionServed(crd *CustomResourceDefinition, version string) bool {
250 for _, v := range crd.Spec.Versions {
251 if !v.Served || v.Name != version {
252 continue
253 }
254 return true
255 }
256 return false
257 }
258
View as plain text