1
16
17 package customresourcedefinition
18
19 import (
20 "context"
21 "fmt"
22
23 "sigs.k8s.io/structured-merge-diff/v4/fieldpath"
24
25 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
26 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation"
27 apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
28 apiequality "k8s.io/apimachinery/pkg/api/equality"
29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30 "k8s.io/apimachinery/pkg/fields"
31 "k8s.io/apimachinery/pkg/labels"
32 "k8s.io/apimachinery/pkg/runtime"
33 "k8s.io/apimachinery/pkg/util/validation/field"
34 "k8s.io/apiserver/pkg/registry/generic"
35 "k8s.io/apiserver/pkg/storage"
36 "k8s.io/apiserver/pkg/storage/names"
37 utilfeature "k8s.io/apiserver/pkg/util/feature"
38 )
39
40
41 type strategy struct {
42 runtime.ObjectTyper
43 names.NameGenerator
44 }
45
46 func NewStrategy(typer runtime.ObjectTyper) strategy {
47 return strategy{typer, names.SimpleNameGenerator}
48 }
49
50 func (strategy) NamespaceScoped() bool {
51 return false
52 }
53
54
55
56 func (strategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
57 fields := map[fieldpath.APIVersion]*fieldpath.Set{
58 "apiextensions.k8s.io/v1": fieldpath.NewSet(
59 fieldpath.MakePathOrDie("status"),
60 ),
61 "apiextensions.k8s.io/v1beta1": fieldpath.NewSet(
62 fieldpath.MakePathOrDie("status"),
63 ),
64 }
65
66 return fields
67 }
68
69
70 func (strategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
71 crd := obj.(*apiextensions.CustomResourceDefinition)
72 crd.Status = apiextensions.CustomResourceDefinitionStatus{}
73 crd.Generation = 1
74
75 for _, v := range crd.Spec.Versions {
76 if v.Storage {
77 if !apiextensions.IsStoredVersion(crd, v.Name) {
78 crd.Status.StoredVersions = append(crd.Status.StoredVersions, v.Name)
79 }
80 break
81 }
82 }
83 dropDisabledFields(crd, nil)
84 }
85
86
87 func (strategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
88 newCRD := obj.(*apiextensions.CustomResourceDefinition)
89 oldCRD := old.(*apiextensions.CustomResourceDefinition)
90 newCRD.Status = oldCRD.Status
91
92
93
94
95
96
97
98
99
100 if !apiequality.Semantic.DeepEqual(oldCRD.Spec, newCRD.Spec) {
101 newCRD.Generation = oldCRD.Generation + 1
102 }
103
104 for _, v := range newCRD.Spec.Versions {
105 if v.Storage {
106 if !apiextensions.IsStoredVersion(newCRD, v.Name) {
107 newCRD.Status.StoredVersions = append(newCRD.Status.StoredVersions, v.Name)
108 }
109 break
110 }
111 }
112 dropDisabledFields(newCRD, oldCRD)
113 }
114
115
116 func (strategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
117 return validation.ValidateCustomResourceDefinition(ctx, obj.(*apiextensions.CustomResourceDefinition))
118 }
119
120
121 func (strategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { return nil }
122
123
124
125 func (strategy) AllowCreateOnUpdate() bool {
126 return false
127 }
128
129
130 func (strategy) AllowUnconditionalUpdate() bool {
131 return false
132 }
133
134
135 func (strategy) Canonicalize(obj runtime.Object) {
136 }
137
138
139 func (strategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
140 return validation.ValidateCustomResourceDefinitionUpdate(ctx, obj.(*apiextensions.CustomResourceDefinition), old.(*apiextensions.CustomResourceDefinition))
141 }
142
143
144 func (strategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
145 return nil
146 }
147
148 type statusStrategy struct {
149 runtime.ObjectTyper
150 names.NameGenerator
151 }
152
153 func NewStatusStrategy(typer runtime.ObjectTyper) statusStrategy {
154 return statusStrategy{typer, names.SimpleNameGenerator}
155 }
156
157 func (statusStrategy) NamespaceScoped() bool {
158 return false
159 }
160
161
162
163 func (statusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
164 fields := map[fieldpath.APIVersion]*fieldpath.Set{
165 "apiextensions.k8s.io/v1": fieldpath.NewSet(
166 fieldpath.MakePathOrDie("metadata"),
167 fieldpath.MakePathOrDie("spec"),
168 ),
169 "apiextensions.k8s.io/v1beta1": fieldpath.NewSet(
170 fieldpath.MakePathOrDie("metadata"),
171 fieldpath.MakePathOrDie("spec"),
172 ),
173 }
174
175 return fields
176 }
177
178 func (statusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
179 newObj := obj.(*apiextensions.CustomResourceDefinition)
180 oldObj := old.(*apiextensions.CustomResourceDefinition)
181 newObj.Spec = oldObj.Spec
182
183
184 metav1.ResetObjectMetaForStatus(&newObj.ObjectMeta, &newObj.ObjectMeta)
185 }
186
187 func (statusStrategy) AllowCreateOnUpdate() bool {
188 return false
189 }
190
191 func (statusStrategy) AllowUnconditionalUpdate() bool {
192 return false
193 }
194
195 func (statusStrategy) Canonicalize(obj runtime.Object) {
196 }
197
198 func (statusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
199 return validation.ValidateUpdateCustomResourceDefinitionStatus(obj.(*apiextensions.CustomResourceDefinition), old.(*apiextensions.CustomResourceDefinition))
200 }
201
202
203 func (statusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
204 return nil
205 }
206
207
208 func GetAttrs(obj runtime.Object) (labels.Set, fields.Set, error) {
209 apiserver, ok := obj.(*apiextensions.CustomResourceDefinition)
210 if !ok {
211 return nil, nil, fmt.Errorf("given object is not a CustomResourceDefinition")
212 }
213 return labels.Set(apiserver.ObjectMeta.Labels), CustomResourceDefinitionToSelectableFields(apiserver), nil
214 }
215
216
217
218 func MatchCustomResourceDefinition(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
219 return storage.SelectionPredicate{
220 Label: label,
221 Field: field,
222 GetAttrs: GetAttrs,
223 }
224 }
225
226
227 func CustomResourceDefinitionToSelectableFields(obj *apiextensions.CustomResourceDefinition) fields.Set {
228 return generic.ObjectMetaFieldsSet(&obj.ObjectMeta, true)
229 }
230
231
232
233 func dropDisabledFields(newCRD *apiextensions.CustomResourceDefinition, oldCRD *apiextensions.CustomResourceDefinition) {
234 if !utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CRDValidationRatcheting) && (oldCRD == nil || (oldCRD != nil && !specHasOptionalOldSelf(&oldCRD.Spec))) {
235 if newCRD.Spec.Validation != nil {
236 dropOptionalOldSelfField(newCRD.Spec.Validation.OpenAPIV3Schema)
237 }
238
239 for _, v := range newCRD.Spec.Versions {
240 if v.Schema != nil {
241 dropOptionalOldSelfField(v.Schema.OpenAPIV3Schema)
242 }
243 }
244 }
245 if !utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceFieldSelectors) && (oldCRD == nil || (oldCRD != nil && !specHasSelectableFields(&oldCRD.Spec))) {
246 dropSelectableFields(&newCRD.Spec)
247 }
248 }
249
250
251 func dropOptionalOldSelfField(schema *apiextensions.JSONSchemaProps) {
252 if schema == nil {
253 return
254 }
255 for i := range schema.XValidations {
256 schema.XValidations[i].OptionalOldSelf = nil
257 }
258
259 if schema.AdditionalProperties != nil {
260 dropOptionalOldSelfField(schema.AdditionalProperties.Schema)
261 }
262 for def, jsonSchema := range schema.Properties {
263 dropOptionalOldSelfField(&jsonSchema)
264 schema.Properties[def] = jsonSchema
265 }
266 if schema.Items != nil {
267 dropOptionalOldSelfField(schema.Items.Schema)
268 for i, jsonSchema := range schema.Items.JSONSchemas {
269 dropOptionalOldSelfField(&jsonSchema)
270 schema.Items.JSONSchemas[i] = jsonSchema
271 }
272 }
273 }
274
275 func specHasOptionalOldSelf(spec *apiextensions.CustomResourceDefinitionSpec) bool {
276 return validation.HasSchemaWith(spec, schemaHasOptionalOldSelf)
277 }
278
279 func schemaHasOptionalOldSelf(s *apiextensions.JSONSchemaProps) bool {
280 return validation.SchemaHas(s, func(s *apiextensions.JSONSchemaProps) bool {
281 for _, v := range s.XValidations {
282 if v.OptionalOldSelf != nil {
283 return true
284 }
285
286 }
287 return false
288 })
289 }
290
291 func dropSelectableFields(spec *apiextensions.CustomResourceDefinitionSpec) {
292 spec.SelectableFields = nil
293 for i := range spec.Versions {
294 spec.Versions[i].SelectableFields = nil
295 }
296 }
297
298 func specHasSelectableFields(spec *apiextensions.CustomResourceDefinitionSpec) bool {
299 if spec.SelectableFields != nil {
300 return true
301 }
302 for _, v := range spec.Versions {
303 if v.SelectableFields != nil {
304 return true
305 }
306 }
307
308 return false
309 }
310
View as plain text