mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 17:30:00 +00:00
bugfix: disable ratcheting for typemeta fields
The metadata fields may undergo pre-processing that often does not reflect the input. Example is `apiVersion` if conversion takes place.
This commit is contained in:
parent
7353f52bc8
commit
20ec766adf
@ -210,8 +210,16 @@ func (r ratchetingOptions) shouldRatchetError() bool {
|
||||
func (r ratchetingOptions) key(field string) ratchetingOptions {
|
||||
if r.currentCorrelation == nil {
|
||||
return r
|
||||
} else if r.nearestParentCorrelation == nil && (field == "apiVersion" || field == "kind") {
|
||||
// We cannot ratchet changes to the APIVersion and kind fields field since
|
||||
// they aren't visible. (both old and new are converted to the same type)
|
||||
//
|
||||
return ratchetingOptions{}
|
||||
}
|
||||
|
||||
// nearestParentCorrelation is always non-nil except for the root node.
|
||||
// The below line ensures that the next nearestParentCorrelation is set
|
||||
// to a non-nil r.currentCorrelation
|
||||
return ratchetingOptions{currentCorrelation: r.currentCorrelation.Key(field), nearestParentCorrelation: r.currentCorrelation}
|
||||
}
|
||||
|
||||
|
@ -3622,6 +3622,106 @@ func TestRatcheting(t *testing.T) {
|
||||
`rule compile error: compilation failed: ERROR: <input>:1:1: undeclared reference to 'asdausidyhASDNJm'`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "typemeta fields are not ratcheted",
|
||||
schema: mustSchema(`
|
||||
type: object
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
x-kubernetes-validations:
|
||||
- rule: self == "v1"
|
||||
kind:
|
||||
type: string
|
||||
x-kubernetes-validations:
|
||||
- rule: self == "Pod"
|
||||
`),
|
||||
oldObj: mustUnstructured(`
|
||||
apiVersion: v2
|
||||
kind: Baz
|
||||
`),
|
||||
newObj: mustUnstructured(`
|
||||
apiVersion: v2
|
||||
kind: Baz
|
||||
`),
|
||||
errors: []string{
|
||||
`root.apiVersion: Invalid value: "string": failed rule: self == "v1"`,
|
||||
`root.kind: Invalid value: "string": failed rule: self == "Pod"`,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nested typemeta fields may still be ratcheted",
|
||||
schema: mustSchema(`
|
||||
type: object
|
||||
properties:
|
||||
list:
|
||||
type: array
|
||||
x-kubernetes-list-type: map
|
||||
x-kubernetes-list-map-keys: ["name"]
|
||||
maxItems: 2
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
apiVersion:
|
||||
type: string
|
||||
x-kubernetes-validations:
|
||||
- rule: self == "v1"
|
||||
kind:
|
||||
type: string
|
||||
x-kubernetes-validations:
|
||||
- rule: self == "Pod"
|
||||
subField:
|
||||
type: object
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
x-kubernetes-validations:
|
||||
- rule: self == "v1"
|
||||
kind:
|
||||
type: string
|
||||
x-kubernetes-validations:
|
||||
- rule: self == "Pod"
|
||||
otherField:
|
||||
type: string
|
||||
`),
|
||||
oldObj: mustUnstructured(`
|
||||
subField:
|
||||
apiVersion: v2
|
||||
kind: Baz
|
||||
list:
|
||||
- name: entry1
|
||||
apiVersion: v2
|
||||
kind: Baz
|
||||
- name: entry2
|
||||
apiVersion: v3
|
||||
kind: Bar
|
||||
`),
|
||||
newObj: mustUnstructured(`
|
||||
subField:
|
||||
apiVersion: v2
|
||||
kind: Baz
|
||||
otherField: newValue
|
||||
list:
|
||||
- name: entry1
|
||||
apiVersion: v2
|
||||
kind: Baz
|
||||
otherField: newValue2
|
||||
- name: entry2
|
||||
apiVersion: v3
|
||||
kind: Bar
|
||||
otherField: newValue3
|
||||
`),
|
||||
warnings: []string{
|
||||
`root.subField.apiVersion: Invalid value: "string": failed rule: self == "v1"`,
|
||||
`root.subField.kind: Invalid value: "string": failed rule: self == "Pod"`,
|
||||
`root.list[0].apiVersion: Invalid value: "string": failed rule: self == "v1"`,
|
||||
`root.list[0].kind: Invalid value: "string": failed rule: self == "Pod"`,
|
||||
`root.list[1].apiVersion: Invalid value: "string": failed rule: self == "v1"`,
|
||||
`root.list[1].kind: Invalid value: "string": failed rule: self == "Pod"`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
|
@ -165,7 +165,13 @@ func (r *ratchetingValueValidator) Validate(new interface{}) *validate.Result {
|
||||
// If the old value cannot be correlated, then default validation is used.
|
||||
func (r *ratchetingValueValidator) SubPropertyValidator(field string, schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, options ...validate.Option) validate.ValueValidator {
|
||||
childNode := r.correlation.Key(field)
|
||||
if childNode == nil {
|
||||
if childNode == nil || (r.path == "" && isTypeMetaField(field)) {
|
||||
// Defer to default validation if we cannot correlate the old value
|
||||
// or if we are validating the root object and the field is a metadata
|
||||
// field.
|
||||
//
|
||||
// We cannot ratchet changes to the APIVersion field since they aren't visible.
|
||||
// (both old and new are converted to the same type)
|
||||
return validate.NewSchemaValidator(schema, rootSchema, root, formats, options...)
|
||||
}
|
||||
|
||||
@ -210,3 +216,7 @@ func (r ratchetingValueValidator) SetPath(path string) {
|
||||
func (r ratchetingValueValidator) Applies(source interface{}, valueKind reflect.Kind) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func isTypeMetaField(path string) bool {
|
||||
return path == "kind" || path == "apiVersion"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user