Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
e2fd72ca
Commit
e2fd72ca
authored
Jun 09, 2019
by
Dr. Stefan Schimanski
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
apiextensions: do not check for pruned defaults under metadata
parent
d06827af
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
371 additions
and
43 deletions
+371
-43
validation.go
...apiserver/pkg/apis/apiextensions/validation/validation.go
+74
-21
validation_test.go
...rver/pkg/apis/apiextensions/validation/validation_test.go
+207
-0
algorithm.go
...ns-apiserver/pkg/apiserver/schema/objectmeta/algorithm.go
+1
-1
validation.go
...piextensions-apiserver/pkg/apiserver/schema/validation.go
+1
-0
objectmeta_test.go
...iextensions-apiserver/test/integration/objectmeta_test.go
+88
-21
No files found.
staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation.go
View file @
e2fd72ca
...
...
@@ -566,6 +566,11 @@ func ValidateCustomResourceColumnDefinition(col *apiextensions.CustomResourceCol
// specStandardValidator applies validations for different OpenAPI specification versions.
type
specStandardValidator
interface
{
validate
(
spec
*
apiextensions
.
JSONSchemaProps
,
fldPath
*
field
.
Path
)
field
.
ErrorList
withForbiddenDefaults
(
reason
string
)
specStandardValidator
// insideResourceMeta returns true when validating either TypeMeta or ObjectMeta, from an embedded resource or on the top-level.
insideResourceMeta
()
bool
withInsideResourceMeta
()
specStandardValidator
}
// ValidateCustomResourceDefinitionValidation statically validates
...
...
@@ -612,7 +617,7 @@ func ValidateCustomResourceDefinitionValidation(customResourceValidation *apiext
openAPIV3Schema
:=
&
specStandardValidatorV3
{
allowDefaults
:
allowDefaults
,
}
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
schema
,
fldPath
.
Child
(
"openAPIV3Schema"
),
openAPIV3Schema
)
...
)
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
schema
,
fldPath
.
Child
(
"openAPIV3Schema"
),
openAPIV3Schema
,
true
)
...
)
if
mustBeStructural
{
if
ss
,
err
:=
structuralschema
.
NewStructural
(
schema
);
err
!=
nil
{
...
...
@@ -636,7 +641,7 @@ func ValidateCustomResourceDefinitionValidation(customResourceValidation *apiext
}
// ValidateCustomResourceDefinitionOpenAPISchema statically validates
func
ValidateCustomResourceDefinitionOpenAPISchema
(
schema
*
apiextensions
.
JSONSchemaProps
,
fldPath
*
field
.
Path
,
ssv
specStandardValidator
)
field
.
ErrorList
{
func
ValidateCustomResourceDefinitionOpenAPISchema
(
schema
*
apiextensions
.
JSONSchemaProps
,
fldPath
*
field
.
Path
,
ssv
specStandardValidator
,
isRoot
bool
)
field
.
ErrorList
{
allErrs
:=
field
.
ErrorList
{}
if
schema
==
nil
{
...
...
@@ -664,53 +669,68 @@ func ValidateCustomResourceDefinitionOpenAPISchema(schema *apiextensions.JSONSch
allErrs
=
append
(
allErrs
,
field
.
Forbidden
(
fldPath
.
Child
(
"additionalProperties"
),
"additionalProperties and properties are mutual exclusive"
))
}
}
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
schema
.
AdditionalProperties
.
Schema
,
fldPath
.
Child
(
"additionalProperties"
),
ssv
)
...
)
// Note: we forbid additionalProperties at resource root, both embedded and top-level.
// But further inside, additionalProperites is possible, e.g. for labels or annotations.
subSsv
:=
ssv
if
ssv
.
insideResourceMeta
()
{
// we have to forbid defaults inside additionalProperties because pruning without actual value is ambiguous
subSsv
=
ssv
.
withForbiddenDefaults
(
"inside additionalProperties applying to object metadata"
)
}
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
schema
.
AdditionalProperties
.
Schema
,
fldPath
.
Child
(
"additionalProperties"
),
subSsv
,
false
)
...
)
}
if
len
(
schema
.
Properties
)
!=
0
{
for
property
,
jsonSchema
:=
range
schema
.
Properties
{
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
&
jsonSchema
,
fldPath
.
Child
(
"properties"
)
.
Key
(
property
),
ssv
)
...
)
subSsv
:=
ssv
if
(
isRoot
||
schema
.
XEmbeddedResource
)
&&
property
==
"metadata"
{
// we recurse into the schema that applies to ObjectMeta.
subSsv
=
ssv
.
withInsideResourceMeta
()
if
isRoot
{
subSsv
=
subSsv
.
withForbiddenDefaults
(
fmt
.
Sprintf
(
"in top-level %s"
,
property
))
}
}
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
&
jsonSchema
,
fldPath
.
Child
(
"properties"
)
.
Key
(
property
),
subSsv
,
false
)
...
)
}
}
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
schema
.
Not
,
fldPath
.
Child
(
"not"
),
ssv
)
...
)
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
schema
.
Not
,
fldPath
.
Child
(
"not"
),
ssv
,
false
)
...
)
if
len
(
schema
.
AllOf
)
!=
0
{
for
i
,
jsonSchema
:=
range
schema
.
AllOf
{
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
&
jsonSchema
,
fldPath
.
Child
(
"allOf"
)
.
Index
(
i
),
ssv
)
...
)
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
&
jsonSchema
,
fldPath
.
Child
(
"allOf"
)
.
Index
(
i
),
ssv
,
false
)
...
)
}
}
if
len
(
schema
.
OneOf
)
!=
0
{
for
i
,
jsonSchema
:=
range
schema
.
OneOf
{
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
&
jsonSchema
,
fldPath
.
Child
(
"oneOf"
)
.
Index
(
i
),
ssv
)
...
)
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
&
jsonSchema
,
fldPath
.
Child
(
"oneOf"
)
.
Index
(
i
),
ssv
,
false
)
...
)
}
}
if
len
(
schema
.
AnyOf
)
!=
0
{
for
i
,
jsonSchema
:=
range
schema
.
AnyOf
{
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
&
jsonSchema
,
fldPath
.
Child
(
"anyOf"
)
.
Index
(
i
),
ssv
)
...
)
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
&
jsonSchema
,
fldPath
.
Child
(
"anyOf"
)
.
Index
(
i
),
ssv
,
false
)
...
)
}
}
if
len
(
schema
.
Definitions
)
!=
0
{
for
definition
,
jsonSchema
:=
range
schema
.
Definitions
{
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
&
jsonSchema
,
fldPath
.
Child
(
"definitions"
)
.
Key
(
definition
),
ssv
)
...
)
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
&
jsonSchema
,
fldPath
.
Child
(
"definitions"
)
.
Key
(
definition
),
ssv
,
false
)
...
)
}
}
if
schema
.
Items
!=
nil
{
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
schema
.
Items
.
Schema
,
fldPath
.
Child
(
"items"
),
ssv
)
...
)
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
schema
.
Items
.
Schema
,
fldPath
.
Child
(
"items"
),
ssv
,
false
)
...
)
if
len
(
schema
.
Items
.
JSONSchemas
)
!=
0
{
for
i
,
jsonSchema
:=
range
schema
.
Items
.
JSONSchemas
{
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
&
jsonSchema
,
fldPath
.
Child
(
"items"
)
.
Index
(
i
),
ssv
)
...
)
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
&
jsonSchema
,
fldPath
.
Child
(
"items"
)
.
Index
(
i
),
ssv
,
false
)
...
)
}
}
}
if
schema
.
Dependencies
!=
nil
{
for
dependency
,
jsonSchemaPropsOrStringArray
:=
range
schema
.
Dependencies
{
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
jsonSchemaPropsOrStringArray
.
Schema
,
fldPath
.
Child
(
"dependencies"
)
.
Key
(
dependency
),
ssv
)
...
)
allErrs
=
append
(
allErrs
,
ValidateCustomResourceDefinitionOpenAPISchema
(
jsonSchemaPropsOrStringArray
.
Schema
,
fldPath
.
Child
(
"dependencies"
)
.
Key
(
dependency
),
ssv
,
false
)
...
)
}
}
...
...
@@ -722,7 +742,26 @@ func ValidateCustomResourceDefinitionOpenAPISchema(schema *apiextensions.JSONSch
}
type
specStandardValidatorV3
struct
{
allowDefaults
bool
allowDefaults
bool
disallowDefaultsReason
string
isInsideResourceMeta
bool
}
func
(
v
*
specStandardValidatorV3
)
withForbiddenDefaults
(
reason
string
)
specStandardValidator
{
clone
:=
*
v
clone
.
disallowDefaultsReason
=
reason
clone
.
allowDefaults
=
false
return
&
clone
}
func
(
v
*
specStandardValidatorV3
)
withInsideResourceMeta
()
specStandardValidator
{
clone
:=
*
v
clone
.
isInsideResourceMeta
=
true
return
&
clone
}
func
(
v
*
specStandardValidatorV3
)
insideResourceMeta
()
bool
{
return
v
.
isInsideResourceMeta
}
// validate validates against OpenAPI Schema v3.
...
...
@@ -741,23 +780,37 @@ func (v *specStandardValidatorV3) validate(schema *apiextensions.JSONSchemaProps
if
v
.
allowDefaults
{
if
s
,
err
:=
structuralschema
.
NewStructural
(
schema
);
err
==
nil
{
// ignore errors here locally. They will show up for the root of the schema.
pruned
:=
runtime
.
DeepCopyJSONValue
(
interface
{}(
*
schema
.
Default
))
pruning
.
Prune
(
pruned
,
s
,
false
)
if
err
:=
schemaobjectmeta
.
Coerce
(
fldPath
,
pruned
,
s
,
false
,
false
);
err
!=
nil
{
clone
:=
runtime
.
DeepCopyJSONValue
(
interface
{}(
*
schema
.
Default
))
if
!
v
.
isInsideResourceMeta
||
s
.
XEmbeddedResource
{
pruning
.
Prune
(
clone
,
s
,
s
.
XEmbeddedResource
)
// If we are under metadata, there are implicitly specified fields like kind, apiVersion, metadata, labels.
// We cannot prune as they are pruned as well. This allows more defaults than we would like to.
// TODO: be precise about pruning under metadata
}
// TODO: coerce correctly if we are not at the object root, but somewhere below.
if
err
:=
schemaobjectmeta
.
Coerce
(
fldPath
,
clone
,
s
,
s
.
XEmbeddedResource
,
false
);
err
!=
nil
{
allErrs
=
append
(
allErrs
,
err
)
}
if
!
reflect
.
DeepEqual
(
pruned
,
*
schema
.
Default
)
{
allErrs
=
append
(
allErrs
,
field
.
Invalid
(
fldPath
.
Child
(
"default"
),
schema
.
Default
,
"must not have unspecified fields"
))
if
!
reflect
.
DeepEqual
(
clone
,
interface
{}(
*
schema
.
Default
))
{
allErrs
=
append
(
allErrs
,
field
.
Invalid
(
fldPath
.
Child
(
"default"
),
schema
.
Default
,
"must not have unknown fields"
))
}
else
if
s
.
XEmbeddedResource
{
// validate an embedded resource
schemaobjectmeta
.
Validate
(
fldPath
,
interface
{}(
*
schema
.
Default
),
nil
,
true
)
}
// validate the default value
. Only validating and pruned defaults are allowed
.
// validate the default value
with user the provided schema
.
validator
:=
govalidate
.
NewSchemaValidator
(
s
.
ToGoOpenAPI
(),
nil
,
""
,
strfmt
.
Default
)
if
err
:=
apiservervalidation
.
ValidateCustomResource
(
pruned
,
validator
);
err
!=
nil
{
if
err
:=
apiservervalidation
.
ValidateCustomResource
(
interface
{}(
*
schema
.
Default
)
,
validator
);
err
!=
nil
{
allErrs
=
append
(
allErrs
,
field
.
Invalid
(
fldPath
.
Child
(
"default"
),
schema
.
Default
,
fmt
.
Sprintf
(
"must validate: %v"
,
err
)))
}
}
}
else
{
allErrs
=
append
(
allErrs
,
field
.
Forbidden
(
fldPath
.
Child
(
"default"
),
"must not be set"
))
detail
:=
"must not be set"
if
len
(
v
.
disallowDefaultsReason
)
>
0
{
detail
+=
" "
+
v
.
disallowDefaultsReason
}
allErrs
=
append
(
allErrs
,
field
.
Forbidden
(
fldPath
.
Child
(
"default"
),
detail
))
}
}
...
...
staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation_test.go
View file @
e2fd72ca
...
...
@@ -1957,6 +1957,213 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
enabledFeatures
:
[]
featuregate
.
Feature
{
features
.
CustomResourceDefaulting
},
},
{
name
:
"metadata defaults"
,
resource
:
&
apiextensions
.
CustomResourceDefinition
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Name
:
"plural.group.com"
},
Spec
:
apiextensions
.
CustomResourceDefinitionSpec
{
Group
:
"group.com"
,
Version
:
"v1"
,
Versions
:
[]
apiextensions
.
CustomResourceDefinitionVersion
{
{
Name
:
"v1"
,
Served
:
true
,
Storage
:
true
,
Schema
:
&
apiextensions
.
CustomResourceValidation
{
OpenAPIV3Schema
:
&
apiextensions
.
JSONSchemaProps
{
Type
:
"object"
,
Properties
:
map
[
string
]
apiextensions
.
JSONSchemaProps
{
"metadata"
:
{
Type
:
"object"
,
// forbidden: no default for top-level metadata
Default
:
jsonPtr
(
map
[
string
]
interface
{}{
"name"
:
"foo"
,
}),
},
"embedded"
:
{
Type
:
"object"
,
XEmbeddedResource
:
true
,
Properties
:
map
[
string
]
apiextensions
.
JSONSchemaProps
{
"metadata"
:
{
Type
:
"object"
,
Default
:
jsonPtr
(
map
[
string
]
interface
{}{
"name"
:
"foo"
,
// TODO: forbid unknown field under metadata
"unknown"
:
int64
(
42
),
}),
},
},
},
},
},
},
},
{
Name
:
"v2"
,
Served
:
true
,
Storage
:
false
,
Schema
:
&
apiextensions
.
CustomResourceValidation
{
OpenAPIV3Schema
:
&
apiextensions
.
JSONSchemaProps
{
Type
:
"object"
,
Properties
:
map
[
string
]
apiextensions
.
JSONSchemaProps
{
"metadata"
:
{
Type
:
"object"
,
Properties
:
map
[
string
]
apiextensions
.
JSONSchemaProps
{
"name"
:
{
Type
:
"string"
,
// forbidden: no default in top-level metadata
Default
:
jsonPtr
(
"foo"
),
},
},
},
"embedded"
:
{
Type
:
"object"
,
XEmbeddedResource
:
true
,
Properties
:
map
[
string
]
apiextensions
.
JSONSchemaProps
{
"apiVersion"
:
{
Type
:
"string"
,
Default
:
jsonPtr
(
"v1"
),
},
"kind"
:
{
Type
:
"string"
,
Default
:
jsonPtr
(
"Pod"
),
},
"metadata"
:
{
Type
:
"object"
,
Properties
:
map
[
string
]
apiextensions
.
JSONSchemaProps
{
"name"
:
{
Type
:
"string"
,
Default
:
jsonPtr
(
"foo"
),
},
},
},
},
},
},
},
},
},
{
Name
:
"v3"
,
Served
:
true
,
Storage
:
false
,
Schema
:
&
apiextensions
.
CustomResourceValidation
{
OpenAPIV3Schema
:
&
apiextensions
.
JSONSchemaProps
{
Type
:
"object"
,
Properties
:
map
[
string
]
apiextensions
.
JSONSchemaProps
{
"embedded"
:
{
Type
:
"object"
,
XEmbeddedResource
:
true
,
Properties
:
map
[
string
]
apiextensions
.
JSONSchemaProps
{
"apiVersion"
:
{
Type
:
"string"
,
Default
:
jsonPtr
(
"v1"
),
},
"kind"
:
{
Type
:
"string"
,
// TODO: forbid non-validating nested values in metadata
Default
:
jsonPtr
(
"%"
),
},
"metadata"
:
{
Type
:
"object"
,
Default
:
jsonPtr
(
map
[
string
]
interface
{}{
"labels"
:
map
[
string
]
interface
{}{
// TODO: forbid non-validating nested field in meta
"bar"
:
"x y"
,
},
}),
},
},
},
},
},
},
},
{
Name
:
"v4"
,
Served
:
true
,
Storage
:
false
,
Schema
:
&
apiextensions
.
CustomResourceValidation
{
OpenAPIV3Schema
:
&
apiextensions
.
JSONSchemaProps
{
Type
:
"object"
,
Properties
:
map
[
string
]
apiextensions
.
JSONSchemaProps
{
"embedded"
:
{
Type
:
"object"
,
XEmbeddedResource
:
true
,
Properties
:
map
[
string
]
apiextensions
.
JSONSchemaProps
{
"metadata"
:
{
Type
:
"object"
,
Properties
:
map
[
string
]
apiextensions
.
JSONSchemaProps
{
"name"
:
{
Type
:
"string"
,
// TODO: forbid wrongly typed nested fields in metadata
Default
:
jsonPtr
(
"%"
),
},
"labels"
:
{
Type
:
"object"
,
Properties
:
map
[
string
]
apiextensions
.
JSONSchemaProps
{
"bar"
:
{
Type
:
"string"
,
// TODO: forbid non-validating nested fields in metadata
Default
:
jsonPtr
(
"x y"
),
},
},
},
"annotations"
:
{
Type
:
"object"
,
AdditionalProperties
:
&
apiextensions
.
JSONSchemaPropsOrBool
{
Schema
:
&
apiextensions
.
JSONSchemaProps
{
Type
:
"string"
,
// forbidden: no default under additionalProperties inside of metadata
Default
:
jsonPtr
(
"abc"
),
},
},
},
},
},
},
},
},
},
},
},
},
Scope
:
apiextensions
.
NamespaceScoped
,
Names
:
apiextensions
.
CustomResourceDefinitionNames
{
Plural
:
"plural"
,
Singular
:
"singular"
,
Kind
:
"Plural"
,
ListKind
:
"PluralList"
,
},
PreserveUnknownFields
:
pointer
.
BoolPtr
(
false
),
},
Status
:
apiextensions
.
CustomResourceDefinitionStatus
{
StoredVersions
:
[]
string
{
"v1"
},
},
},
errors
:
[]
validationMatch
{
// Forbidden: must not be set in top-level metadata
forbidden
(
"spec"
,
"versions[0]"
,
"schema"
,
"openAPIV3Schema"
,
"properties[metadata]"
,
"default"
),
// Invalid value: map[string]interface {}{"name":"foo", "unknown":42}: must not have unknown fields
// TODO: invalid("spec", "versions[0]", "schema", "openAPIV3Schema", "properties[embedded]", "properties[metadata]", "default"),
// Forbidden: must not be set in top-level metadata
forbidden
(
"spec"
,
"versions[1]"
,
"schema"
,
"openAPIV3Schema"
,
"properties[metadata]"
,
"properties[name]"
,
"default"
),
// Invalid value: "x y"
// TODO: invalid("spec", "versions[2]", "schema", "openAPIV3Schema", "properties[embedded]", "properties[metadata]", "default"),
// Invalid value: "%": kind: Invalid value: "%"
// TODO: invalid("spec", "versions[2]", "schema", "openAPIV3Schema", "properties[embedded]", "properties[kind]", "default"),
// Invalid value: "%"
// TODO: invalid("spec", "versions[3]", "schema", "openAPIV3Schema", "properties[embedded]", "properties[metadata]", "properties[labels]", "properties[bar]", "default"),
// Invalid value: "x y"
// TODO: invalid("spec", "versions[3]", "schema", "openAPIV3Schema", "properties[embedded]", "properties[metadata]", "properties[name]", "default"),
// Forbidden: must not be set inside additionalProperties applying to object metadata
forbidden
(
"spec"
,
"versions[3]"
,
"schema"
,
"openAPIV3Schema"
,
"properties[embedded]"
,
"properties[metadata]"
,
"properties[annotations]"
,
"additionalProperties"
,
"default"
),
},
enabledFeatures
:
[]
featuregate
.
Feature
{
features
.
CustomResourceDefaulting
},
},
{
name
:
"contradicting meta field types"
,
resource
:
&
apiextensions
.
CustomResourceDefinition
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Name
:
"plural.group.com"
},
...
...
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta/algorithm.go
View file @
e2fd72ca
...
...
@@ -55,7 +55,7 @@ func (c *coercer) coerce(pth *field.Path, x interface{}, s *structuralschema.Str
if
_
,
ok
:=
v
.
(
string
);
!
ok
&&
c
.
dropInvalidFields
{
delete
(
x
,
k
)
}
else
if
!
ok
{
return
field
.
Invalid
(
pth
,
v
,
"must be a string"
)
return
field
.
Invalid
(
pth
.
Child
(
k
)
,
v
,
"must be a string"
)
}
case
"metadata"
:
meta
,
found
,
err
:=
GetObjectMeta
(
x
,
c
.
dropInvalidFields
)
...
...
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/validation.go
View file @
e2fd72ca
...
...
@@ -160,6 +160,7 @@ func validateStructuralInvariants(s *Structural, lvl level, fldPath *field.Path)
metadata
.
Properties
=
nil
}
metadata
.
Type
=
""
metadata
.
Default
.
Object
=
nil
// this is checked in API validation (and also tested)
if
metadata
.
ValueValidation
==
nil
{
metadata
.
ValueValidation
=
&
ValueValidation
{}
}
...
...
staging/src/k8s.io/apiextensions-apiserver/test/integration/objectmeta_test.go
View file @
e2fd72ca
...
...
@@ -29,6 +29,7 @@ import (
apiextensionsv1beta1
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
serveroptions
"k8s.io/apiextensions-apiserver/pkg/cmd/server/options"
"k8s.io/apiextensions-apiserver/pkg/features"
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
"k8s.io/apimachinery/pkg/api/errors"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
...
...
@@ -37,7 +38,9 @@ import (
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/json"
genericapirequest
"k8s.io/apiserver/pkg/endpoints/request"
utilfeature
"k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/dynamic"
featuregatetesting
"k8s.io/component-base/featuregate/testing"
"k8s.io/utils/pointer"
)
...
...
@@ -262,7 +265,7 @@ var embeddedResourceFixture = &apiextensionsv1beta1.CustomResourceDefinition{
ListKind
:
"FooList"
,
},
Scope
:
apiextensionsv1beta1
.
ClusterScoped
,
PreserveUnknownFields
:
pointer
.
BoolPtr
(
tru
e
),
PreserveUnknownFields
:
pointer
.
BoolPtr
(
fals
e
),
Subresources
:
&
apiextensionsv1beta1
.
CustomResourceSubresources
{
Status
:
&
apiextensionsv1beta1
.
CustomResourceSubresourceStatus
{},
},
...
...
@@ -276,22 +279,47 @@ properties:
embedded:
type: object
x-kubernetes-embedded-resource: true
x-kubernetes-preserve-unknown-fields: true
noEmbeddedObject:
type: object
x-kubernetes-preserve-unknown-fields: true
embeddedNested:
type: object
x-kubernetes-embedded-resource: true
x-kubernetes-preserve-unknown-fields: true
properties:
embedded:
type: object
x-kubernetes-embedded-resource: true
x-kubernetes-preserve-unknown-fields: true
defaults:
type: object
x-kubernetes-embedded-resource: true
x-kubernetes-preserve-unknown-fields: true
default:
apiVersion: v1
kind: Pod
labels:
foo: bar
invalidDefaults:
type: object
properties:
embedded:
type: object
x-kubernetes-embedded-resource: true
x-kubernetes-preserve-unknown-fields: true
default:
apiVersion: "foo/v1"
kind: "%"
metadata:
labels:
foo: bar
abc: "x y"
`
embeddedResourceInstance
=
`
kind: Foo
apiVersion: tests.apiextensions.k8s.io/v1beta1
metadata:
name: foo
embedded:
apiVersion: foo/v1
kind: Foo
...
...
@@ -342,13 +370,16 @@ embeddedNested:
kind: Foo
metadata:
name: foo
defaults:
apiVersion: v1
kind: Pod
labels:
foo: bar
`
wronglyTypedEmbeddedResourceInstance
=
`
kind: Foo
apiVersion: tests.apiextensions.k8s.io/v1beta1
metadata:
name: invalid
embedded:
apiVersion: foo/v1
kind: Foo
...
...
@@ -360,8 +391,6 @@ embedded:
invalidEmbeddedResourceInstance
=
`
kind: Foo
apiVersion: tests.apiextensions.k8s.io/v1beta1
metadata:
name: invalid
embedded:
apiVersion: foo/v1
kind: "%"
...
...
@@ -377,10 +406,13 @@ embeddedNested:
kind: "%"
metadata:
name: ..
invalidDefaults: {}
`
)
func
TestEmbeddedResources
(
t
*
testing
.
T
)
{
defer
featuregatetesting
.
SetFeatureGateDuringTest
(
t
,
utilfeature
.
DefaultFeatureGate
,
features
.
CustomResourceDefaulting
,
true
)()
tearDownFn
,
apiExtensionClient
,
dynamicClient
,
err
:=
fixtures
.
StartDefaultServerWithClients
(
t
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
...
...
@@ -404,6 +436,7 @@ func TestEmbeddedResources(t *testing.T) {
if
err
:=
yaml
.
Unmarshal
([]
byte
(
embeddedResourceInstance
),
&
foo
.
Object
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
unstructured
.
SetNestedField
(
foo
.
Object
,
"foo"
,
"metadata"
,
"name"
)
foo
,
err
=
fooClient
.
Create
(
foo
,
metav1
.
CreateOptions
{})
if
err
!=
nil
{
t
.
Fatalf
(
"Unable to create CR: %v"
,
err
)
...
...
@@ -421,11 +454,12 @@ func TestEmbeddedResources(t *testing.T) {
}
t
.
Logf
(
"Trying to create wrongly typed CR"
)
invali
d
:=
&
unstructured
.
Unstructured
{}
if
err
:=
yaml
.
Unmarshal
([]
byte
(
wronglyTypedEmbeddedResourceInstance
),
&
invali
d
.
Object
);
err
!=
nil
{
wronglyType
d
:=
&
unstructured
.
Unstructured
{}
if
err
:=
yaml
.
Unmarshal
([]
byte
(
wronglyTypedEmbeddedResourceInstance
),
&
wronglyType
d
.
Object
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
_
,
err
=
fooClient
.
Create
(
invalid
,
metav1
.
CreateOptions
{})
unstructured
.
SetNestedField
(
wronglyTyped
.
Object
,
"invalid"
,
"metadata"
,
"name"
)
_
,
err
=
fooClient
.
Create
(
wronglyTyped
,
metav1
.
CreateOptions
{})
if
err
==
nil
{
t
.
Fatal
(
"Expected creation to fail, but didn't"
)
}
...
...
@@ -440,24 +474,57 @@ func TestEmbeddedResources(t *testing.T) {
}
t
.
Logf
(
"Trying to create invalid CR"
)
wronglyType
d
:=
&
unstructured
.
Unstructured
{}
if
err
:=
yaml
.
Unmarshal
([]
byte
(
invalidEmbeddedResourceInstance
),
&
wronglyType
d
.
Object
);
err
!=
nil
{
invali
d
:=
&
unstructured
.
Unstructured
{}
if
err
:=
yaml
.
Unmarshal
([]
byte
(
invalidEmbeddedResourceInstance
),
&
invali
d
.
Object
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
_
,
err
=
fooClient
.
Create
(
wronglyTyped
,
metav1
.
CreateOptions
{})
unstructured
.
SetNestedField
(
invalid
.
Object
,
"invalid"
,
"metadata"
,
"name"
)
unstructured
.
SetNestedField
(
invalid
.
Object
,
"x y"
,
"metadata"
,
"labels"
,
"foo"
)
_
,
err
=
fooClient
.
Create
(
invalid
,
metav1
.
CreateOptions
{})
if
err
==
nil
{
t
.
Fatal
(
"Expected creation to fail, but didn't"
)
}
t
.
Logf
(
"Creation of invalid object failed with: %v"
,
err
)
for
_
,
s
:=
range
[]
string
{
`embedded.kind: Invalid value: "%"`
,
`embedded.metadata.name: Invalid value: ".."`
,
`embeddedNested.kind: Invalid value: "%"`
,
`embeddedNested.metadata.name: Invalid value: ".."`
,
`embeddedNested.embedded.kind: Invalid value: "%"`
,
`embeddedNested.embedded.metadata.name: Invalid value: ".."`
,
}
{
invalidErrors
:=
[]
string
{
`[metadata.labels: Invalid value: "x y"`
,
` embedded.kind: Invalid value: "%"`
,
` embedded.metadata.name: Invalid value: ".."`
,
` embeddedNested.kind: Invalid value: "%"`
,
` embeddedNested.metadata.name: Invalid value: ".."`
,
` embeddedNested.embedded.kind: Invalid value: "%"`
,
` embeddedNested.embedded.metadata.name: Invalid value: ".."`
,
` invalidDefaults.embedded.kind: Invalid value: "%"`
,
` invalidDefaults.embedded.metadata.labels: Invalid value: "x y"`
,
}
for
_
,
s
:=
range
invalidErrors
{
if
!
strings
.
Contains
(
err
.
Error
(),
s
)
{
t
.
Errorf
(
"missing error: %s"
,
s
)
}
}
t
.
Logf
(
"Creating a valid CR and then updating it with invalid values, expecting the same errors"
)
valid
:=
&
unstructured
.
Unstructured
{}
if
err
:=
yaml
.
Unmarshal
([]
byte
(
embeddedResourceInstance
),
&
valid
.
Object
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
unstructured
.
SetNestedField
(
valid
.
Object
,
"valid"
,
"metadata"
,
"name"
)
valid
,
err
=
fooClient
.
Create
(
valid
,
metav1
.
CreateOptions
{})
if
err
!=
nil
{
t
.
Fatalf
(
"Unable to create CR: %v"
,
err
)
}
for
k
,
v
:=
range
invalid
.
Object
{
if
k
==
"metadata"
{
continue
}
valid
.
Object
[
k
]
=
v
}
unstructured
.
SetNestedField
(
valid
.
Object
,
"x y"
,
"metadata"
,
"labels"
,
"foo"
)
if
_
,
err
=
fooClient
.
Update
(
valid
,
metav1
.
UpdateOptions
{});
err
==
nil
{
t
.
Fatal
(
"Expected update error, but got none"
)
}
t
.
Logf
(
"Update failed with: %v"
,
err
)
for
_
,
s
:=
range
invalidErrors
{
if
!
strings
.
Contains
(
err
.
Error
(),
s
)
{
t
.
Errorf
(
"missing error: %s"
,
s
)
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment