Skip to content

Commit 318587e

Browse files
authored
Merge pull request #200 from jpbetz/fix-102749
Don't process schemaless fields with ReconcileFieldSetWithSchema
2 parents ea1021d + e54e56c commit 318587e

File tree

2 files changed

+190
-7
lines changed

2 files changed

+190
-7
lines changed

typed/reconcile_schema.go

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,6 @@ func ReconcileFieldSetWithSchema(fieldset *fieldpath.Set, tv *TypedValue) (*fiel
124124
v.schema = tv.schema
125125
v.typeRef = tv.typeRef
126126

127-
// We don't reconcile deduced types, which are primarily for use by unstructured CRDs. Deduced
128-
// types do not support atomic or granular tags. Nor does the dynamic schema deduction
129-
// interact well with the reconcile logic.
130-
if v.schema == DeducedParseableType.Schema {
131-
return nil, nil
132-
}
133-
134127
defer v.finished()
135128
errs := v.reconcile()
136129

@@ -229,6 +222,12 @@ func (v *reconcileWithSchemaWalker) visitMapItems(t *schema.Map, element *fieldp
229222
}
230223

231224
func (v *reconcileWithSchemaWalker) doMap(t *schema.Map) (errs ValidationErrors) {
225+
// We don't currently reconcile deduced types (unstructured CRDs) or maps that contain only unknown
226+
// fields since deduced types do not yet support atomic or granular tags.
227+
if isUntypedDeducedMap(t) {
228+
return errs
229+
}
230+
232231
// reconcile maps and structs changed from granular to atomic.
233232
// Note that migrations from atomic to granular are not recommended and will
234233
// be treated as if they were always granular.
@@ -274,3 +273,18 @@ func typeRefAtPath(t *schema.Map, pe fieldpath.PathElement) (schema.TypeRef, boo
274273
}
275274
return tr, tr != schema.TypeRef{}
276275
}
276+
277+
// isUntypedDeducedMap returns true if m has no fields defined, but allows untyped elements.
278+
// This is equivalent to a openAPI object that has x-kubernetes-preserve-unknown-fields=true
279+
// but does not have any properties defined on the object.
280+
func isUntypedDeducedMap(m *schema.Map) bool {
281+
return isUntypedDeducedRef(m.ElementType) && m.Fields == nil
282+
}
283+
284+
func isUntypedDeducedRef(t schema.TypeRef) bool {
285+
if t.NamedType != nil {
286+
return *t.NamedType == "__untyped_deduced_"
287+
}
288+
atom := t.Inlined
289+
return atom.Scalar != nil && *atom.Scalar == "untyped"
290+
}

typed/reconcile_schema_test.go

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,66 @@ func granularSchema(version string) typed.YAMLObject {
9898
- name: numeric
9999
type:
100100
scalar: numeric
101+
- name: empty
102+
map:
103+
elementType:
104+
scalar: untyped
105+
list:
106+
elementType:
107+
namedType: __untyped_atomic_
108+
elementRelationship: atomic
109+
map:
110+
elementType:
111+
namedType: __untyped_deduced_
112+
elementRelationship: separable
113+
- name: emptyWithPreserveUnknown
114+
map:
115+
fields:
116+
- name: preserveField
117+
type:
118+
map:
119+
elementType:
120+
scalar: untyped
121+
list:
122+
elementType:
123+
namedType: __untyped_atomic_
124+
elementRelationship: atomic
125+
map:
126+
elementType:
127+
namedType: __untyped_deduced_
128+
elementRelationship: separable
129+
- name: populatedWithPreserveUnknown
130+
map:
131+
fields:
132+
- name: preserveField
133+
type:
134+
map:
135+
fields:
136+
- name: list
137+
type:
138+
namedType: list
139+
elementType:
140+
namedType: __untyped_deduced_
141+
- name: __untyped_atomic_
142+
scalar: untyped
143+
list:
144+
elementType:
145+
namedType: __untyped_atomic_
146+
elementRelationship: atomic
147+
map:
148+
elementType:
149+
namedType: __untyped_atomic_
150+
elementRelationship: atomic
151+
- name: __untyped_deduced_
152+
scalar: untyped
153+
list:
154+
elementType:
155+
namedType: __untyped_atomic_
156+
elementRelationship: atomic
157+
map:
158+
elementType:
159+
namedType: __untyped_deduced_
160+
elementRelationship: separable
101161
`, version))
102162
}
103163

@@ -164,6 +224,66 @@ func atomicSchema(version string) typed.YAMLObject {
164224
- name: numeric
165225
type:
166226
scalar: numeric
227+
- name: empty
228+
map:
229+
elementType:
230+
scalar: untyped
231+
list:
232+
elementType:
233+
namedType: __untyped_atomic_
234+
elementRelationship: atomic
235+
map:
236+
elementType:
237+
namedType: __untyped_deduced_
238+
elementRelationship: separable
239+
- name: emptyWithPreserveUnknown
240+
map:
241+
fields:
242+
- name: preserveField
243+
type:
244+
map:
245+
elementType:
246+
scalar: untyped
247+
list:
248+
elementType:
249+
namedType: __untyped_atomic_
250+
elementRelationship: atomic
251+
map:
252+
elementType:
253+
namedType: __untyped_deduced_
254+
elementRelationship: separable
255+
- name: populatedWithPreserveUnknown
256+
map:
257+
fields:
258+
- name: preserveField
259+
type:
260+
map:
261+
fields:
262+
- name: list
263+
type:
264+
namedType: list
265+
elementType:
266+
namedType: __untyped_deduced_
267+
- name: __untyped_atomic_
268+
scalar: untyped
269+
list:
270+
elementType:
271+
namedType: __untyped_atomic_
272+
elementRelationship: atomic
273+
map:
274+
elementType:
275+
namedType: __untyped_atomic_
276+
elementRelationship: atomic
277+
- name: __untyped_deduced_
278+
scalar: untyped
279+
list:
280+
elementType:
281+
namedType: __untyped_atomic_
282+
elementRelationship: atomic
283+
map:
284+
elementType:
285+
namedType: __untyped_deduced_
286+
elementRelationship: separable
167287
`, version))
168288
}
169289

@@ -257,6 +377,55 @@ unchanged: {}
257377
_P("unchanged"),
258378
),
259379
fixedFields: nil, // indicates no change
380+
}, {
381+
name: "deduced",
382+
rootTypeName: "empty",
383+
oldSchema: atomicSchema("v1"),
384+
newSchema: granularSchema("v1"),
385+
liveObject: basicLiveObject,
386+
oldFields: _NS(
387+
_P("struct"),
388+
_P("list"),
389+
_P("objectList"),
390+
_P("unchanged", "numeric"),
391+
),
392+
fixedFields: nil, // indicates no change
393+
}, {
394+
name: "empty-preserve-unknown",
395+
rootTypeName: "emptyWithPreserveUnknown",
396+
oldSchema: atomicSchema("v1"),
397+
newSchema: granularSchema("v1"),
398+
liveObject: typed.YAMLObject(`
399+
preserveField:
400+
arbitrary: abc
401+
`),
402+
oldFields: _NS(
403+
_P("preserveField"),
404+
_P("preserveField", "arbitrary"),
405+
),
406+
fixedFields: nil, // indicates no change
407+
}, {
408+
name: "populated-preserve-unknown",
409+
rootTypeName: "populatedWithPreserveUnknown",
410+
oldSchema: granularSchema("v1"),
411+
newSchema: atomicSchema("v1"),
412+
liveObject: typed.YAMLObject(`
413+
preserveField:
414+
arbitrary: abc
415+
list:
416+
- one
417+
`),
418+
oldFields: _NS(
419+
_P("preserveField"),
420+
_P("preserveField", "arbitrary"),
421+
_P("preserveField", "list"),
422+
_P("preserveField", "list", _V("one")),
423+
),
424+
fixedFields: _NS(
425+
_P("preserveField"),
426+
_P("preserveField", "arbitrary"),
427+
_P("preserveField", "list"),
428+
),
260429
}}
261430

262431
func TestReconcileFieldSetWithSchema(t *testing.T) {

0 commit comments

Comments
 (0)