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
1db0024e
Commit
1db0024e
authored
May 31, 2018
by
Dr. Stefan Schimanski
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
apiextensions: add ObjectMeta schema validation and pruning
parent
d02cf08e
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
886 additions
and
16 deletions
+886
-16
BUILD
pkg/apis/apps/fuzzer/BUILD
+1
-0
fuzzer.go
pkg/apis/apps/fuzzer/fuzzer.go
+7
-0
fuzzer.go
pkg/apis/batch/fuzzer/fuzzer.go
+8
-0
fuzzer.go
pkg/apis/core/fuzzer/fuzzer.go
+13
-0
BUILD
pkg/apis/extensions/fuzzer/BUILD
+1
-0
fuzzer.go
pkg/apis/extensions/fuzzer/fuzzer.go
+32
-0
Godeps.json
...ing/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json
+8
-0
BUILD
...ng/src/k8s.io/apiextensions-apiserver/pkg/apiserver/BUILD
+26
-10
customresource_handler.go
...ensions-apiserver/pkg/apiserver/customresource_handler.go
+173
-2
customresource_handler_test.go
...ns-apiserver/pkg/apiserver/customresource_handler_test.go
+184
-1
jsonpath_test.go
...io/apiextensions-apiserver/pkg/apiserver/jsonpath_test.go
+235
-0
BUILD
...src/k8s.io/apiextensions-apiserver/test/integration/BUILD
+4
-0
objectmeta_test.go
...iextensions-apiserver/test/integration/objectmeta_test.go
+166
-0
fuzzer.go
...ng/src/k8s.io/apimachinery/pkg/apis/meta/fuzzer/fuzzer.go
+28
-3
No files found.
pkg/apis/apps/fuzzer/BUILD
View file @
1db0024e
...
@@ -12,6 +12,7 @@ go_library(
...
@@ -12,6 +12,7 @@ go_library(
deps = [
deps = [
"//pkg/apis/apps:go_default_library",
"//pkg/apis/apps:go_default_library",
"//vendor/github.com/google/gofuzz:go_default_library",
"//vendor/github.com/google/gofuzz:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
],
],
)
)
...
...
pkg/apis/apps/fuzzer/fuzzer.go
View file @
1db0024e
...
@@ -19,6 +19,7 @@ package fuzzer
...
@@ -19,6 +19,7 @@ package fuzzer
import
(
import
(
fuzz
"github.com/google/gofuzz"
fuzz
"github.com/google/gofuzz"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
runtimeserializer
"k8s.io/apimachinery/pkg/runtime/serializer"
runtimeserializer
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/kubernetes/pkg/apis/apps"
"k8s.io/kubernetes/pkg/apis/apps"
)
)
...
@@ -46,6 +47,12 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
...
@@ -46,6 +47,12 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
if
s
.
Status
.
CollisionCount
==
nil
{
if
s
.
Status
.
CollisionCount
==
nil
{
s
.
Status
.
CollisionCount
=
new
(
int32
)
s
.
Status
.
CollisionCount
=
new
(
int32
)
}
}
if
s
.
Spec
.
Selector
==
nil
{
s
.
Spec
.
Selector
=
&
metav1
.
LabelSelector
{
MatchLabels
:
s
.
Spec
.
Template
.
Labels
}
}
if
len
(
s
.
Labels
)
==
0
{
s
.
Labels
=
s
.
Spec
.
Template
.
Labels
}
},
},
}
}
}
}
pkg/apis/batch/fuzzer/fuzzer.go
View file @
1db0024e
...
@@ -32,6 +32,14 @@ func newBool(val bool) *bool {
...
@@ -32,6 +32,14 @@ func newBool(val bool) *bool {
// Funcs returns the fuzzer functions for the batch api group.
// Funcs returns the fuzzer functions for the batch api group.
var
Funcs
=
func
(
codecs
runtimeserializer
.
CodecFactory
)
[]
interface
{}
{
var
Funcs
=
func
(
codecs
runtimeserializer
.
CodecFactory
)
[]
interface
{}
{
return
[]
interface
{}{
return
[]
interface
{}{
func
(
j
*
batch
.
Job
,
c
fuzz
.
Continue
)
{
c
.
FuzzNoCustom
(
j
)
// fuzz self without calling this function again
// match defaulting
if
len
(
j
.
Labels
)
==
0
{
j
.
Labels
=
j
.
Spec
.
Template
.
Labels
}
},
func
(
j
*
batch
.
JobSpec
,
c
fuzz
.
Continue
)
{
func
(
j
*
batch
.
JobSpec
,
c
fuzz
.
Continue
)
{
c
.
FuzzNoCustom
(
j
)
// fuzz self without calling this function again
c
.
FuzzNoCustom
(
j
)
// fuzz self without calling this function again
completions
:=
int32
(
c
.
Rand
.
Int31
())
completions
:=
int32
(
c
.
Rand
.
Int31
())
...
...
pkg/apis/core/fuzzer/fuzzer.go
View file @
1db0024e
...
@@ -93,6 +93,19 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
...
@@ -93,6 +93,19 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
c
.
Fuzz
(
&
j
.
ObjectMeta
)
c
.
Fuzz
(
&
j
.
ObjectMeta
)
j
.
Target
.
Name
=
c
.
RandString
()
j
.
Target
.
Name
=
c
.
RandString
()
},
},
func
(
j
*
core
.
ReplicationController
,
c
fuzz
.
Continue
)
{
c
.
FuzzNoCustom
(
j
)
// match defaulting
if
j
.
Spec
.
Template
!=
nil
{
if
len
(
j
.
Labels
)
==
0
{
j
.
Labels
=
j
.
Spec
.
Template
.
Labels
}
if
len
(
j
.
Spec
.
Selector
)
==
0
{
j
.
Spec
.
Selector
=
j
.
Spec
.
Template
.
Labels
}
}
},
func
(
j
*
core
.
ReplicationControllerSpec
,
c
fuzz
.
Continue
)
{
func
(
j
*
core
.
ReplicationControllerSpec
,
c
fuzz
.
Continue
)
{
c
.
FuzzNoCustom
(
j
)
// fuzz self without calling this function again
c
.
FuzzNoCustom
(
j
)
// fuzz self without calling this function again
//j.TemplateRef = nil // this is required for round trip
//j.TemplateRef = nil // this is required for round trip
...
...
pkg/apis/extensions/fuzzer/BUILD
View file @
1db0024e
...
@@ -12,6 +12,7 @@ go_library(
...
@@ -12,6 +12,7 @@ go_library(
deps = [
deps = [
"//pkg/apis/extensions:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//vendor/github.com/google/gofuzz:go_default_library",
"//vendor/github.com/google/gofuzz:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
],
],
...
...
pkg/apis/extensions/fuzzer/fuzzer.go
View file @
1db0024e
...
@@ -21,6 +21,7 @@ import (
...
@@ -21,6 +21,7 @@ import (
fuzz
"github.com/google/gofuzz"
fuzz
"github.com/google/gofuzz"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
runtimeserializer
"k8s.io/apimachinery/pkg/runtime/serializer"
runtimeserializer
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/extensions"
...
@@ -29,6 +30,17 @@ import (
...
@@ -29,6 +30,17 @@ import (
// Funcs returns the fuzzer functions for the extensions api group.
// Funcs returns the fuzzer functions for the extensions api group.
var
Funcs
=
func
(
codecs
runtimeserializer
.
CodecFactory
)
[]
interface
{}
{
var
Funcs
=
func
(
codecs
runtimeserializer
.
CodecFactory
)
[]
interface
{}
{
return
[]
interface
{}{
return
[]
interface
{}{
func
(
j
*
extensions
.
Deployment
,
c
fuzz
.
Continue
)
{
c
.
FuzzNoCustom
(
j
)
// match defaulting
if
j
.
Spec
.
Selector
==
nil
{
j
.
Spec
.
Selector
=
&
metav1
.
LabelSelector
{
MatchLabels
:
j
.
Spec
.
Template
.
Labels
}
}
if
len
(
j
.
Labels
)
==
0
{
j
.
Labels
=
j
.
Spec
.
Template
.
Labels
}
},
func
(
j
*
extensions
.
DeploymentSpec
,
c
fuzz
.
Continue
)
{
func
(
j
*
extensions
.
DeploymentSpec
,
c
fuzz
.
Continue
)
{
c
.
FuzzNoCustom
(
j
)
// fuzz self without calling this function again
c
.
FuzzNoCustom
(
j
)
// fuzz self without calling this function again
rhl
:=
int32
(
c
.
Rand
.
Int31
())
rhl
:=
int32
(
c
.
Rand
.
Int31
())
...
@@ -54,6 +66,15 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
...
@@ -54,6 +66,15 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
j
.
RollingUpdate
=
&
rollingUpdate
j
.
RollingUpdate
=
&
rollingUpdate
}
}
},
},
func
(
j
*
extensions
.
DaemonSet
,
c
fuzz
.
Continue
)
{
c
.
FuzzNoCustom
(
j
)
// match defaulter
j
.
Spec
.
Template
.
Generation
=
0
if
len
(
j
.
ObjectMeta
.
Labels
)
==
0
{
j
.
ObjectMeta
.
Labels
=
j
.
Spec
.
Template
.
ObjectMeta
.
Labels
}
},
func
(
j
*
extensions
.
DaemonSetSpec
,
c
fuzz
.
Continue
)
{
func
(
j
*
extensions
.
DaemonSetSpec
,
c
fuzz
.
Continue
)
{
c
.
FuzzNoCustom
(
j
)
// fuzz self without calling this function again
c
.
FuzzNoCustom
(
j
)
// fuzz self without calling this function again
rhl
:=
int32
(
c
.
Rand
.
Int31
())
rhl
:=
int32
(
c
.
Rand
.
Int31
())
...
@@ -78,5 +99,16 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
...
@@ -78,5 +99,16 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
j
.
RollingUpdate
=
&
rollingUpdate
j
.
RollingUpdate
=
&
rollingUpdate
}
}
},
},
func
(
j
*
extensions
.
ReplicaSet
,
c
fuzz
.
Continue
)
{
c
.
FuzzNoCustom
(
j
)
// match defaulter
if
j
.
Spec
.
Selector
==
nil
{
j
.
Spec
.
Selector
=
&
metav1
.
LabelSelector
{
MatchLabels
:
j
.
Spec
.
Template
.
Labels
}
}
if
len
(
j
.
Labels
)
==
0
{
j
.
Labels
=
j
.
Spec
.
Template
.
Labels
}
},
}
}
}
}
staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json
View file @
1db0024e
...
@@ -2083,6 +2083,10 @@
...
@@ -2083,6 +2083,10 @@
"Rev"
:
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"Rev"
:
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
},
{
{
"ImportPath"
:
"k8s.io/apimachinery/pkg/apis/meta/fuzzer"
,
"Rev"
:
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath"
:
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
,
"ImportPath"
:
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
,
"Rev"
:
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"Rev"
:
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
},
...
@@ -2271,6 +2275,10 @@
...
@@ -2271,6 +2275,10 @@
"Rev"
:
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"Rev"
:
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
},
{
{
"ImportPath"
:
"k8s.io/client-go/kubernetes/scheme"
,
"Rev"
:
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{
"ImportPath"
:
"k8s.io/client-go/rest"
,
"ImportPath"
:
"k8s.io/client-go/rest"
,
"Rev"
:
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
"Rev"
:
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
},
...
...
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/BUILD
View file @
1db0024e
...
@@ -15,6 +15,7 @@ go_library(
...
@@ -15,6 +15,7 @@ go_library(
"customresource_handler.go",
"customresource_handler.go",
],
],
importpath = "k8s.io/apiextensions-apiserver/pkg/apiserver",
importpath = "k8s.io/apiextensions-apiserver/pkg/apiserver",
visibility = ["//visibility:public"],
deps = [
deps = [
"//vendor/github.com/go-openapi/spec:go_default_library",
"//vendor/github.com/go-openapi/spec:go_default_library",
"//vendor/github.com/go-openapi/strfmt:go_default_library",
"//vendor/github.com/go-openapi/strfmt:go_default_library",
...
@@ -75,6 +76,30 @@ go_library(
...
@@ -75,6 +76,30 @@ go_library(
],
],
)
)
go_test(
name = "go_default_test",
srcs = [
"customresource_handler_test.go",
"jsonpath_test.go",
],
embed = [":go_default_library"],
deps = [
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library",
"//vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/conversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/testing/fuzzer:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/fuzzer:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/scheme:go_default_library",
],
)
filegroup(
filegroup(
name = "package-srcs",
name = "package-srcs",
srcs = glob(["**"]),
srcs = glob(["**"]),
...
@@ -90,14 +115,5 @@ filegroup(
...
@@ -90,14 +115,5 @@ filegroup(
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation:all-srcs",
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation:all-srcs",
],
],
tags = ["automanaged"],
tags = ["automanaged"],
)
visibility = ["//visibility:public"],
go_test(
name = "go_default_test",
srcs = ["customresource_handler_test.go"],
embed = [":go_default_library"],
deps = [
"//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library",
"//vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/conversion:go_default_library",
],
)
)
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go
View file @
1db0024e
...
@@ -17,6 +17,7 @@ limitations under the License.
...
@@ -17,6 +17,7 @@ limitations under the License.
package
apiserver
package
apiserver
import
(
import
(
encodingjson
"encoding/json"
"fmt"
"fmt"
"net/http"
"net/http"
"path"
"path"
...
@@ -610,7 +611,8 @@ func (s unstructuredNegotiatedSerializer) EncoderForVersion(encoder runtime.Enco
...
@@ -610,7 +611,8 @@ func (s unstructuredNegotiatedSerializer) EncoderForVersion(encoder runtime.Enco
}
}
func
(
s
unstructuredNegotiatedSerializer
)
DecoderToVersion
(
decoder
runtime
.
Decoder
,
gv
runtime
.
GroupVersioner
)
runtime
.
Decoder
{
func
(
s
unstructuredNegotiatedSerializer
)
DecoderToVersion
(
decoder
runtime
.
Decoder
,
gv
runtime
.
GroupVersioner
)
runtime
.
Decoder
{
return
versioning
.
NewDefaultingCodecForScheme
(
Scheme
,
nil
,
decoder
,
nil
,
gv
)
d
:=
schemaCoercingDecoder
{
delegate
:
decoder
,
validator
:
unstructuredSchemaCoercer
{}}
return
versioning
.
NewDefaultingCodecForScheme
(
Scheme
,
nil
,
d
,
nil
,
gv
)
}
}
type
UnstructuredObjectTyper
struct
{
type
UnstructuredObjectTyper
struct
{
...
@@ -704,7 +706,176 @@ type crdConversionRESTOptionsGetter struct {
...
@@ -704,7 +706,176 @@ type crdConversionRESTOptionsGetter struct {
func
(
t
crdConversionRESTOptionsGetter
)
GetRESTOptions
(
resource
schema
.
GroupResource
)
(
generic
.
RESTOptions
,
error
)
{
func
(
t
crdConversionRESTOptionsGetter
)
GetRESTOptions
(
resource
schema
.
GroupResource
)
(
generic
.
RESTOptions
,
error
)
{
ret
,
err
:=
t
.
RESTOptionsGetter
.
GetRESTOptions
(
resource
)
ret
,
err
:=
t
.
RESTOptionsGetter
.
GetRESTOptions
(
resource
)
if
err
==
nil
{
if
err
==
nil
{
ret
.
StorageConfig
.
Codec
=
versioning
.
NewCodec
(
ret
.
StorageConfig
.
Codec
,
ret
.
StorageConfig
.
Codec
,
t
.
converter
,
&
unstructuredCreator
{},
discovery
.
NewUnstructuredObjectTyper
(),
&
unstructuredDefaulter
{
delegate
:
Scheme
},
t
.
encoderVersion
,
t
.
decoderVersion
)
d
:=
schemaCoercingDecoder
{
delegate
:
ret
.
StorageConfig
.
Codec
,
validator
:
unstructuredSchemaCoercer
{
// drop invalid fields while decoding old CRs (before we had any ObjectMeta validation)
dropInvalidMetadata
:
true
,
}}
c
:=
schemaCoercingConverter
{
delegate
:
t
.
converter
,
validator
:
unstructuredSchemaCoercer
{}}
ret
.
StorageConfig
.
Codec
=
versioning
.
NewCodec
(
ret
.
StorageConfig
.
Codec
,
d
,
c
,
&
unstructuredCreator
{},
discovery
.
NewUnstructuredObjectTyper
(),
&
unstructuredDefaulter
{
delegate
:
Scheme
},
t
.
encoderVersion
,
t
.
decoderVersion
)
}
}
return
ret
,
err
return
ret
,
err
}
}
// schemaCoercingDecoder calls the delegate decoder, and then applies the Unstructured schema validator
// to coerce the schema.
type
schemaCoercingDecoder
struct
{
delegate
runtime
.
Decoder
validator
unstructuredSchemaCoercer
}
var
_
runtime
.
Decoder
=
schemaCoercingDecoder
{}
func
(
d
schemaCoercingDecoder
)
Decode
(
data
[]
byte
,
defaults
*
schema
.
GroupVersionKind
,
into
runtime
.
Object
)
(
runtime
.
Object
,
*
schema
.
GroupVersionKind
,
error
)
{
obj
,
gvk
,
err
:=
d
.
delegate
.
Decode
(
data
,
defaults
,
into
)
if
err
!=
nil
{
return
nil
,
gvk
,
err
}
if
u
,
ok
:=
obj
.
(
*
unstructured
.
Unstructured
);
ok
{
if
err
:=
d
.
validator
.
apply
(
u
);
err
!=
nil
{
return
nil
,
gvk
,
err
}
}
return
obj
,
gvk
,
nil
}
// schemaCoercingConverter calls the delegate converter and applies the Unstructured validator to
// coerce the schema.
type
schemaCoercingConverter
struct
{
delegate
runtime
.
ObjectConvertor
validator
unstructuredSchemaCoercer
}
var
_
runtime
.
ObjectConvertor
=
schemaCoercingConverter
{}
func
(
v
schemaCoercingConverter
)
Convert
(
in
,
out
,
context
interface
{})
error
{
if
err
:=
v
.
delegate
.
Convert
(
in
,
out
,
context
);
err
!=
nil
{
return
err
}
if
u
,
ok
:=
out
.
(
*
unstructured
.
Unstructured
);
ok
{
if
err
:=
v
.
validator
.
apply
(
u
);
err
!=
nil
{
return
err
}
}
return
nil
}
func
(
v
schemaCoercingConverter
)
ConvertToVersion
(
in
runtime
.
Object
,
gv
runtime
.
GroupVersioner
)
(
runtime
.
Object
,
error
)
{
out
,
err
:=
v
.
delegate
.
ConvertToVersion
(
in
,
gv
)
if
err
!=
nil
{
return
nil
,
err
}
if
u
,
ok
:=
out
.
(
*
unstructured
.
Unstructured
);
ok
{
if
err
:=
v
.
validator
.
apply
(
u
);
err
!=
nil
{
return
nil
,
err
}
}
return
out
,
nil
}
func
(
v
schemaCoercingConverter
)
ConvertFieldLabel
(
version
,
kind
,
label
,
value
string
)
(
string
,
string
,
error
)
{
return
v
.
ConvertFieldLabel
(
version
,
kind
,
label
,
value
)
}
// unstructuredSchemaCoercer does the validation for Unstructured that json.Unmarshal
// does for native types. This includes:
// - validating and pruning ObjectMeta (here with optional error instead of pruning)
// - TODO: application of an OpenAPI validator (against the whole object or a top-level field of it).
// - TODO: optionally application of post-validation algorithms like defaulting and/or OpenAPI based pruning.
type
unstructuredSchemaCoercer
struct
{
dropInvalidMetadata
bool
}
func
(
v
*
unstructuredSchemaCoercer
)
apply
(
u
*
unstructured
.
Unstructured
)
error
{
// save implicit meta fields that don't have to be specified in the validation spec
kind
,
foundKind
,
err
:=
unstructured
.
NestedString
(
u
.
UnstructuredContent
(),
"kind"
)
if
err
!=
nil
{
return
err
}
apiVersion
,
foundApiVersion
,
err
:=
unstructured
.
NestedString
(
u
.
UnstructuredContent
(),
"apiVersion"
)
if
err
!=
nil
{
return
err
}
objectMeta
,
foundObjectMeta
,
err
:=
getObjectMeta
(
u
,
v
.
dropInvalidMetadata
)
if
err
!=
nil
{
return
err
}
// restore meta fields, starting clean
if
foundKind
{
u
.
SetKind
(
kind
)
}
if
foundApiVersion
{
u
.
SetAPIVersion
(
apiVersion
)
}
if
foundObjectMeta
{
if
err
:=
setObjectMeta
(
u
,
objectMeta
);
err
!=
nil
{
return
err
}
}
return
nil
}
func
getObjectMeta
(
u
*
unstructured
.
Unstructured
,
dropMalformedFields
bool
)
(
*
metav1
.
ObjectMeta
,
bool
,
error
)
{
metadata
,
found
:=
u
.
UnstructuredContent
()[
"metadata"
]
if
!
found
{
return
nil
,
false
,
nil
}
// round-trip through JSON first, hoping that unmarshaling just works
objectMeta
:=
&
metav1
.
ObjectMeta
{}
metadataBytes
,
err
:=
encodingjson
.
Marshal
(
metadata
)
if
err
!=
nil
{
return
nil
,
false
,
err
}
if
err
=
encodingjson
.
Unmarshal
(
metadataBytes
,
objectMeta
);
err
==
nil
{
// if successful, return
return
objectMeta
,
true
,
nil
}
if
!
dropMalformedFields
{
// if we're not trying to drop malformed fields, return the error
return
nil
,
true
,
err
}
metadataMap
,
ok
:=
metadata
.
(
map
[
string
]
interface
{})
if
!
ok
{
return
nil
,
false
,
fmt
.
Errorf
(
"invalid metadata: expected object, got %T"
,
metadata
)
}
// Go field by field accumulating into the metadata object.
// This takes advantage of the fact that you can repeatedly unmarshal individual fields into a single struct,
// each iteration preserving the old key-values.
accumulatedObjectMeta
:=
&
metav1
.
ObjectMeta
{}
testObjectMeta
:=
&
metav1
.
ObjectMeta
{}
for
k
,
v
:=
range
metadataMap
{
// serialize a single field
if
singleFieldBytes
,
err
:=
encodingjson
.
Marshal
(
map
[
string
]
interface
{}{
k
:
v
});
err
==
nil
{
// do a test unmarshal
if
encodingjson
.
Unmarshal
(
singleFieldBytes
,
testObjectMeta
)
==
nil
{
// if that succeeds, unmarshal for real
encodingjson
.
Unmarshal
(
singleFieldBytes
,
accumulatedObjectMeta
)
}
}
}
return
accumulatedObjectMeta
,
true
,
nil
}
func
setObjectMeta
(
u
*
unstructured
.
Unstructured
,
objectMeta
*
metav1
.
ObjectMeta
)
error
{
if
objectMeta
==
nil
{
unstructured
.
RemoveNestedField
(
u
.
UnstructuredContent
(),
"metadata"
)
return
nil
}
metadata
,
err
:=
runtime
.
DefaultUnstructuredConverter
.
ToUnstructured
(
objectMeta
)
if
err
!=
nil
{
return
err
}
u
.
UnstructuredContent
()[
"metadata"
]
=
metadata
return
nil
}
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler_test.go
View file @
1db0024e
...
@@ -17,10 +17,24 @@ limitations under the License.
...
@@ -17,10 +17,24 @@ limitations under the License.
package
apiserver
package
apiserver
import
(
import
(
encodingjson
"encoding/json"
"math/rand"
"reflect"
"testing"
"testing"
corev1
"k8s.io/api/core/v1"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
conversion
"k8s.io/apiextensions-apiserver/pkg/apiserver/conversion"
"k8s.io/apiextensions-apiserver/pkg/apiserver/conversion"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/testing/fuzzer"
metafuzzer
"k8s.io/apimachinery/pkg/apis/meta/fuzzer"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/runtime/serializer/json"
"k8s.io/apimachinery/pkg/util/diff"
clientgoscheme
"k8s.io/client-go/kubernetes/scheme"
)
)
func
TestConvertFieldLabel
(
t
*
testing
.
T
)
{
func
TestConvertFieldLabel
(
t
*
testing
.
T
)
{
...
@@ -94,3 +108,172 @@ func TestConvertFieldLabel(t *testing.T) {
...
@@ -94,3 +108,172 @@ func TestConvertFieldLabel(t *testing.T) {
})
})
}
}
}
}
func
TestRoundtripObjectMeta
(
t
*
testing
.
T
)
{
scheme
:=
runtime
.
NewScheme
()
codecs
:=
serializer
.
NewCodecFactory
(
scheme
)
codec
:=
json
.
NewSerializer
(
json
.
DefaultMetaFactory
,
scheme
,
scheme
,
false
)
seed
:=
rand
.
Int63
()
fuzzer
:=
fuzzer
.
FuzzerFor
(
metafuzzer
.
Funcs
,
rand
.
NewSource
(
seed
),
codecs
)
N
:=
1000
for
i
:=
0
;
i
<
N
;
i
++
{
u
:=
&
unstructured
.
Unstructured
{
Object
:
map
[
string
]
interface
{}{}}
original
:=
&
metav1
.
ObjectMeta
{}
fuzzer
.
Fuzz
(
original
)
if
err
:=
setObjectMeta
(
u
,
original
);
err
!=
nil
{
t
.
Fatalf
(
"unexpected error setting ObjectMeta: %v"
,
err
)
}
o
,
_
,
err
:=
getObjectMeta
(
u
,
false
)
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error getting the Objectmeta: %v"
,
err
)
}
if
!
equality
.
Semantic
.
DeepEqual
(
original
,
o
)
{
t
.
Errorf
(
"diff: %v
\n
Codec: %#v"
,
diff
.
ObjectReflectDiff
(
original
,
o
),
codec
)
}
}
}
// TestMalformedObjectMetaFields sets a number of different random values and types for all
// metadata fields. If encoding/json.Unmarshal accepts them, compare that getObjectMeta
// gives the same result. Otherwise, drop malformed fields.
func
TestMalformedObjectMetaFields
(
t
*
testing
.
T
)
{
fuzzer
:=
fuzzer
.
FuzzerFor
(
metafuzzer
.
Funcs
,
rand
.
NewSource
(
rand
.
Int63
()),
serializer
.
NewCodecFactory
(
runtime
.
NewScheme
()))
spuriousValues
:=
func
()
[]
interface
{}
{
return
[]
interface
{}{
// primitives
nil
,
int64
(
1
),
float64
(
1.5
),
true
,
"a"
,
// well-formed complex values
[]
interface
{}{
"a"
,
"b"
},
map
[
string
]
interface
{}{
"a"
:
"1"
,
"b"
:
"2"
},
[]
interface
{}{
int64
(
1
),
int64
(
2
)},
[]
interface
{}{
float64
(
1.5
),
float64
(
2.5
)},
// known things json decoding tolerates
map
[
string
]
interface
{}{
"a"
:
"1"
,
"b"
:
nil
},
// malformed things
map
[
string
]
interface
{}{
"a"
:
"1"
,
"b"
:
[]
interface
{}{
"nested"
}},
[]
interface
{}{
"a"
,
int64
(
1
),
float64
(
1.5
),
true
,
[]
interface
{}{
"nested"
}},
}
}
N
:=
100
for
i
:=
0
;
i
<
N
;
i
++
{
fuzzedObjectMeta
:=
&
metav1
.
ObjectMeta
{}
fuzzer
.
Fuzz
(
fuzzedObjectMeta
)
goodMetaMap
,
err
:=
runtime
.
DefaultUnstructuredConverter
.
ToUnstructured
(
fuzzedObjectMeta
.
DeepCopy
())
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
for
_
,
pth
:=
range
jsonPaths
(
nil
,
goodMetaMap
)
{
for
_
,
v
:=
range
spuriousValues
()
{
// skip values of same type, because they can only cause decoding errors further insides
orig
,
err
:=
JsonPathValue
(
goodMetaMap
,
pth
,
0
)
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected to not find something at %v: %v"
,
pth
,
err
)
}
if
reflect
.
TypeOf
(
v
)
==
reflect
.
TypeOf
(
orig
)
{
continue
}
// make a spurious map
spuriousMetaMap
,
err
:=
runtime
.
DefaultUnstructuredConverter
.
ToUnstructured
(
fuzzedObjectMeta
.
DeepCopy
())
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
err
:=
SetJsonPath
(
spuriousMetaMap
,
pth
,
0
,
v
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
// See if it can unmarshal to object meta
spuriousJSON
,
err
:=
encodingjson
.
Marshal
(
spuriousMetaMap
)
if
err
!=
nil
{
t
.
Fatalf
(
"error on %v=%#v: %v"
,
pth
,
v
,
err
)
}
expectedObjectMeta
:=
&
metav1
.
ObjectMeta
{}
if
err
:=
encodingjson
.
Unmarshal
(
spuriousJSON
,
expectedObjectMeta
);
err
!=
nil
{
// if standard json unmarshal would fail decoding this field, drop the field entirely
truncatedMetaMap
,
err
:=
runtime
.
DefaultUnstructuredConverter
.
ToUnstructured
(
fuzzedObjectMeta
.
DeepCopy
())
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
// we expect this logic for the different fields:
switch
{
default
:
// delete complete top-level field by default
DeleteJsonPath
(
truncatedMetaMap
,
pth
[
:
1
],
0
)
}
truncatedJSON
,
err
:=
encodingjson
.
Marshal
(
truncatedMetaMap
)
if
err
!=
nil
{
t
.
Fatalf
(
"error on %v=%#v: %v"
,
pth
,
v
,
err
)
}
expectedObjectMeta
=
&
metav1
.
ObjectMeta
{}
if
err
:=
encodingjson
.
Unmarshal
(
truncatedJSON
,
expectedObjectMeta
);
err
!=
nil
{
t
.
Fatalf
(
"error on %v=%#v: %v"
,
pth
,
v
,
err
)
}
}
// make sure dropInvalidTypedFields+getObjectMeta matches what we expect
u
:=
&
unstructured
.
Unstructured
{
Object
:
map
[
string
]
interface
{}{
"metadata"
:
spuriousMetaMap
}}
actualObjectMeta
,
_
,
err
:=
getObjectMeta
(
u
,
true
)
if
err
!=
nil
{
t
.
Errorf
(
"got unexpected error after dropping invalid typed fields on %v=%#v: %v"
,
pth
,
v
,
err
)
continue
}
if
!
equality
.
Semantic
.
DeepEqual
(
expectedObjectMeta
,
actualObjectMeta
)
{
t
.
Errorf
(
"%v=%#v, diff: %v
\n
"
,
pth
,
v
,
diff
.
ObjectReflectDiff
(
expectedObjectMeta
,
actualObjectMeta
))
t
.
Errorf
(
"expectedObjectMeta %#v"
,
expectedObjectMeta
)
}
}
}
}
}
func
TestGetObjectMetaNils
(
t
*
testing
.
T
)
{
u
:=
&
unstructured
.
Unstructured
{
Object
:
map
[
string
]
interface
{}{
"kind"
:
"Pod"
,
"apiVersion"
:
"v1"
,
"metadata"
:
map
[
string
]
interface
{}{
"generateName"
:
nil
,
"labels"
:
map
[
string
]
interface
{}{
"foo"
:
nil
,
},
},
},
}
o
,
_
,
err
:=
getObjectMeta
(
u
,
true
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
if
o
.
GenerateName
!=
""
{
t
.
Errorf
(
"expected null json value to be read as
\"\"
string, but got: %q"
,
o
.
GenerateName
)
}
if
got
,
expected
:=
o
.
Labels
,
map
[
string
]
string
{
"foo"
:
""
};
!
reflect
.
DeepEqual
(
got
,
expected
)
{
t
.
Errorf
(
"unexpected labels, expected=%#v, got=%#v"
,
expected
,
got
)
}
// double check this what the kube JSON decode is doing
bs
,
_
:=
encodingjson
.
Marshal
(
u
.
UnstructuredContent
())
kubeObj
,
_
,
err
:=
clientgoscheme
.
Codecs
.
UniversalDecoder
(
corev1
.
SchemeGroupVersion
)
.
Decode
(
bs
,
nil
,
nil
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
pod
,
ok
:=
kubeObj
.
(
*
corev1
.
Pod
)
if
!
ok
{
t
.
Fatalf
(
"expected v1 Pod, got: %T"
,
pod
)
}
if
got
,
expected
:=
o
.
GenerateName
,
pod
.
ObjectMeta
.
GenerateName
;
got
!=
expected
{
t
.
Errorf
(
"expected generatedName to be %q, got %q"
,
expected
,
got
)
}
if
got
,
expected
:=
o
.
Labels
,
pod
.
ObjectMeta
.
Labels
;
!
reflect
.
DeepEqual
(
got
,
expected
)
{
t
.
Errorf
(
"expected labels to be %v, got %v"
,
expected
,
got
)
}
}
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/jsonpath_test.go
0 → 100644
View file @
1db0024e
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package
apiserver
import
(
"bytes"
"fmt"
"k8s.io/apimachinery/pkg/runtime"
)
type
(
jsonPathNode
struct
{
index
*
int
field
string
}
JsonPath
[]
jsonPathNode
)
func
(
p
JsonPath
)
String
()
string
{
var
buf
bytes
.
Buffer
for
_
,
n
:=
range
p
{
if
n
.
index
==
nil
{
buf
.
WriteString
(
"."
+
n
.
field
)
}
else
{
buf
.
WriteString
(
fmt
.
Sprintf
(
"[%d]"
,
*
n
.
index
))
}
}
return
buf
.
String
()
}
func
jsonPaths
(
base
JsonPath
,
j
map
[
string
]
interface
{})
[]
JsonPath
{
res
:=
make
([]
JsonPath
,
0
,
len
(
j
))
for
k
,
old
:=
range
j
{
kPth
:=
append
(
append
([]
jsonPathNode
(
nil
),
base
...
),
jsonPathNode
{
field
:
k
})
res
=
append
(
res
,
kPth
)
switch
old
:=
old
.
(
type
)
{
case
map
[
string
]
interface
{}
:
res
=
append
(
res
,
jsonPaths
(
kPth
,
old
)
...
)
case
[]
interface
{}
:
res
=
append
(
res
,
jsonIterSlice
(
kPth
,
old
)
...
)
}
}
return
res
}
func
jsonIterSlice
(
base
JsonPath
,
j
[]
interface
{})
[]
JsonPath
{
res
:=
make
([]
JsonPath
,
0
,
len
(
j
))
for
i
,
old
:=
range
j
{
index
:=
i
iPth
:=
append
(
append
([]
jsonPathNode
(
nil
),
base
...
),
jsonPathNode
{
index
:
&
index
})
res
=
append
(
res
,
iPth
)
switch
old
:=
old
.
(
type
)
{
case
map
[
string
]
interface
{}
:
res
=
append
(
res
,
jsonPaths
(
iPth
,
old
)
...
)
case
[]
interface
{}
:
res
=
append
(
res
,
jsonIterSlice
(
iPth
,
old
)
...
)
}
}
return
res
}
func
JsonPathValue
(
j
map
[
string
]
interface
{},
pth
JsonPath
,
base
int
)
(
interface
{},
error
)
{
if
len
(
pth
)
==
base
{
return
nil
,
fmt
.
Errorf
(
"empty json path is invalid for object"
)
}
if
pth
[
base
]
.
index
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"index json path is invalid for object"
)
}
field
,
ok
:=
j
[
pth
[
base
]
.
field
]
if
!
ok
||
len
(
pth
)
==
base
+
1
{
if
len
(
pth
)
>
base
+
1
{
return
nil
,
fmt
.
Errorf
(
"invalid non-terminal json path %q for non-existing field"
,
pth
)
}
return
j
[
pth
[
base
]
.
field
],
nil
}
switch
field
:=
field
.
(
type
)
{
case
map
[
string
]
interface
{}
:
return
JsonPathValue
(
field
,
pth
,
base
+
1
)
case
[]
interface
{}
:
return
jsonPathValueSlice
(
field
,
pth
,
base
+
1
)
default
:
return
nil
,
fmt
.
Errorf
(
"invalid non-terminal json path %q for field"
,
pth
[
:
base
+
1
])
}
}
func
jsonPathValueSlice
(
j
[]
interface
{},
pth
JsonPath
,
base
int
)
(
interface
{},
error
)
{
if
len
(
pth
)
==
base
{
return
nil
,
fmt
.
Errorf
(
"empty json path %q is invalid for object"
,
pth
)
}
if
pth
[
base
]
.
index
==
nil
{
return
nil
,
fmt
.
Errorf
(
"field json path %q is invalid for object"
,
pth
[
:
base
+
1
])
}
if
*
pth
[
base
]
.
index
>=
len
(
j
)
{
return
nil
,
fmt
.
Errorf
(
"invalid index %q for array of size %d"
,
pth
[
:
base
+
1
],
len
(
j
))
}
if
len
(
pth
)
==
base
+
1
{
return
j
[
*
pth
[
base
]
.
index
],
nil
}
switch
item
:=
j
[
*
pth
[
base
]
.
index
]
.
(
type
)
{
case
map
[
string
]
interface
{}
:
return
JsonPathValue
(
item
,
pth
,
base
+
1
)
case
[]
interface
{}
:
return
jsonPathValueSlice
(
item
,
pth
,
base
+
1
)
default
:
return
nil
,
fmt
.
Errorf
(
"invalid non-terminal json path %q for index"
,
pth
[
:
base
+
1
])
}
}
func
SetJsonPath
(
j
map
[
string
]
interface
{},
pth
JsonPath
,
base
int
,
value
interface
{})
error
{
if
len
(
pth
)
==
base
{
return
fmt
.
Errorf
(
"empty json path is invalid for object"
)
}
if
pth
[
base
]
.
index
!=
nil
{
return
fmt
.
Errorf
(
"index json path is invalid for object"
)
}
field
,
ok
:=
j
[
pth
[
base
]
.
field
]
if
!
ok
||
len
(
pth
)
==
base
+
1
{
if
len
(
pth
)
>
base
+
1
{
return
fmt
.
Errorf
(
"invalid non-terminal json path %q for non-existing field"
,
pth
)
}
j
[
pth
[
base
]
.
field
]
=
runtime
.
DeepCopyJSONValue
(
value
)
return
nil
}
switch
field
:=
field
.
(
type
)
{
case
map
[
string
]
interface
{}
:
return
SetJsonPath
(
field
,
pth
,
base
+
1
,
value
)
case
[]
interface
{}
:
return
setJsonPathSlice
(
field
,
pth
,
base
+
1
,
value
)
default
:
return
fmt
.
Errorf
(
"invalid non-terminal json path %q for field"
,
pth
[
:
base
+
1
])
}
}
func
setJsonPathSlice
(
j
[]
interface
{},
pth
JsonPath
,
base
int
,
value
interface
{})
error
{
if
len
(
pth
)
==
base
{
return
fmt
.
Errorf
(
"empty json path %q is invalid for object"
,
pth
)
}
if
pth
[
base
]
.
index
==
nil
{
return
fmt
.
Errorf
(
"field json path %q is invalid for object"
,
pth
[
:
base
+
1
])
}
if
*
pth
[
base
]
.
index
>=
len
(
j
)
{
return
fmt
.
Errorf
(
"invalid index %q for array of size %d"
,
pth
[
:
base
+
1
],
len
(
j
))
}
if
len
(
pth
)
==
base
+
1
{
j
[
*
pth
[
base
]
.
index
]
=
runtime
.
DeepCopyJSONValue
(
value
)
return
nil
}
switch
item
:=
j
[
*
pth
[
base
]
.
index
]
.
(
type
)
{
case
map
[
string
]
interface
{}
:
return
SetJsonPath
(
item
,
pth
,
base
+
1
,
value
)
case
[]
interface
{}
:
return
setJsonPathSlice
(
item
,
pth
,
base
+
1
,
value
)
default
:
return
fmt
.
Errorf
(
"invalid non-terminal json path %q for index"
,
pth
[
:
base
+
1
])
}
}
func
DeleteJsonPath
(
j
map
[
string
]
interface
{},
pth
JsonPath
,
base
int
)
error
{
if
len
(
pth
)
==
base
{
return
fmt
.
Errorf
(
"empty json path is invalid for object"
)
}
if
pth
[
base
]
.
index
!=
nil
{
return
fmt
.
Errorf
(
"index json path is invalid for object"
)
}
field
,
ok
:=
j
[
pth
[
base
]
.
field
]
if
!
ok
||
len
(
pth
)
==
base
+
1
{
if
len
(
pth
)
>
base
+
1
{
return
fmt
.
Errorf
(
"invalid non-terminal json path %q for non-existing field"
,
pth
)
}
delete
(
j
,
pth
[
base
]
.
field
)
return
nil
}
switch
field
:=
field
.
(
type
)
{
case
map
[
string
]
interface
{}
:
return
DeleteJsonPath
(
field
,
pth
,
base
+
1
)
case
[]
interface
{}
:
if
len
(
pth
)
==
base
+
2
{
if
pth
[
base
+
1
]
.
index
==
nil
{
return
fmt
.
Errorf
(
"field json path %q is invalid for object"
,
pth
)
}
j
[
pth
[
base
]
.
field
]
=
append
(
field
[
:*
pth
[
base
+
1
]
.
index
],
field
[
*
pth
[
base
+
1
]
.
index
+
1
:
]
...
)
return
nil
}
return
deleteJsonPathSlice
(
field
,
pth
,
base
+
1
)
default
:
return
fmt
.
Errorf
(
"invalid non-terminal json path %q for field"
,
pth
[
:
base
+
1
])
}
}
func
deleteJsonPathSlice
(
j
[]
interface
{},
pth
JsonPath
,
base
int
)
error
{
if
len
(
pth
)
==
base
{
return
fmt
.
Errorf
(
"empty json path %q is invalid for object"
,
pth
)
}
if
pth
[
base
]
.
index
==
nil
{
return
fmt
.
Errorf
(
"field json path %q is invalid for object"
,
pth
[
:
base
+
1
])
}
if
*
pth
[
base
]
.
index
>=
len
(
j
)
{
return
fmt
.
Errorf
(
"invalid index %q for array of size %d"
,
pth
[
:
base
+
1
],
len
(
j
))
}
if
len
(
pth
)
==
base
+
1
{
return
fmt
.
Errorf
(
"cannot delete item at index %q in-place"
,
pth
[
:
base
])
}
switch
item
:=
j
[
*
pth
[
base
]
.
index
]
.
(
type
)
{
case
map
[
string
]
interface
{}
:
return
DeleteJsonPath
(
item
,
pth
,
base
+
1
)
case
[]
interface
{}
:
if
len
(
pth
)
==
base
+
2
{
if
pth
[
base
+
1
]
.
index
==
nil
{
return
fmt
.
Errorf
(
"field json path %q is invalid for object"
,
pth
)
}
j
[
*
pth
[
base
]
.
index
]
=
append
(
item
[
:*
pth
[
base
+
1
]
.
index
],
item
[
*
pth
[
base
+
1
]
.
index
+
1
:
])
return
nil
}
return
deleteJsonPathSlice
(
item
,
pth
,
base
+
1
)
default
:
return
fmt
.
Errorf
(
"invalid non-terminal json path %q for index"
,
pth
[
:
base
+
1
])
}
}
staging/src/k8s.io/apiextensions-apiserver/test/integration/BUILD
View file @
1db0024e
...
@@ -11,6 +11,7 @@ go_test(
...
@@ -11,6 +11,7 @@ go_test(
srcs = [
srcs = [
"basic_test.go",
"basic_test.go",
"finalization_test.go",
"finalization_test.go",
"objectmeta_test.go",
"registration_test.go",
"registration_test.go",
"subresources_test.go",
"subresources_test.go",
"table_test.go",
"table_test.go",
...
@@ -22,6 +23,7 @@ go_test(
...
@@ -22,6 +23,7 @@ go_test(
tags = ["integration"],
tags = ["integration"],
deps = [
deps = [
"//vendor/github.com/coreos/etcd/clientv3:go_default_library",
"//vendor/github.com/coreos/etcd/clientv3:go_default_library",
"//vendor/github.com/coreos/etcd/pkg/transport:go_default_library",
"//vendor/github.com/ghodss/yaml:go_default_library",
"//vendor/github.com/ghodss/yaml:go_default_library",
"//vendor/github.com/stretchr/testify/require:go_default_library",
"//vendor/github.com/stretchr/testify/require:go_default_library",
"//vendor/k8s.io/api/autoscaling/v1:go_default_library",
"//vendor/k8s.io/api/autoscaling/v1:go_default_library",
...
@@ -38,8 +40,10 @@ go_test(
...
@@ -38,8 +40,10 @@ go_test(
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/client-go/dynamic:go_default_library",
"//vendor/k8s.io/client-go/dynamic:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library",
],
],
...
...
staging/src/k8s.io/apiextensions-apiserver/test/integration/objectmeta_test.go
0 → 100644
View file @
1db0024e
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package
integration
import
(
"path"
"reflect"
"strings"
"testing"
"github.com/coreos/etcd/clientv3"
"github.com/coreos/etcd/pkg/transport"
apiextensionsv1beta1
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apiextensions-apiserver/test/integration/testserver"
"k8s.io/apimachinery/pkg/api/errors"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/json"
genericapirequest
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/client-go/dynamic"
)
func
TestPostInvalidObjectMeta
(
t
*
testing
.
T
)
{
stopCh
,
apiExtensionClient
,
dynamicClient
,
err
:=
testserver
.
StartDefaultServerWithClients
()
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
defer
close
(
stopCh
)
noxuDefinition
:=
testserver
.
NewNoxuCustomResourceDefinition
(
apiextensionsv1beta1
.
NamespaceScoped
)
noxuDefinition
,
err
=
testserver
.
CreateNewCustomResourceDefinition
(
noxuDefinition
,
apiExtensionClient
,
dynamicClient
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
noxuResourceClient
:=
newNamespacedCustomResourceClient
(
"default"
,
dynamicClient
,
noxuDefinition
)
obj
:=
testserver
.
NewNoxuInstance
(
"default"
,
"foo"
)
unstructured
.
SetNestedField
(
obj
.
UnstructuredContent
(),
int64
(
42
),
"metadata"
,
"unknown"
)
unstructured
.
SetNestedField
(
obj
.
UnstructuredContent
(),
map
[
string
]
interface
{}{
"foo"
:
int64
(
42
),
"bar"
:
"abc"
},
"metadata"
,
"labels"
)
_
,
err
=
instantiateCustomResource
(
t
,
obj
,
noxuResourceClient
,
noxuDefinition
)
if
err
==
nil
{
t
.
Fatalf
(
"unexpected non-error, expected invalid labels to be rejected: %v"
,
err
)
}
if
status
,
ok
:=
err
.
(
errors
.
APIStatus
);
!
ok
{
t
.
Fatalf
(
"expected APIStatus error, but got: %#v"
,
err
)
}
else
if
!
errors
.
IsBadRequest
(
err
)
{
t
.
Fatalf
(
"expected BadRequst error, but got: %v"
,
errors
.
ReasonForError
(
err
))
}
else
if
!
strings
.
Contains
(
status
.
Status
()
.
Message
,
"json: cannot unmarshal"
)
{
t
.
Fatalf
(
"expected 'json: cannot unmarshal' error message, got: %v"
,
status
.
Status
()
.
Message
)
}
unstructured
.
SetNestedField
(
obj
.
UnstructuredContent
(),
map
[
string
]
interface
{}{
"bar"
:
"abc"
},
"metadata"
,
"labels"
)
obj
,
err
=
instantiateCustomResource
(
t
,
obj
,
noxuResourceClient
,
noxuDefinition
)
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
if
unknown
,
found
,
err
:=
unstructured
.
NestedInt64
(
obj
.
UnstructuredContent
(),
"metadata"
,
"unknown"
);
err
!=
nil
{
t
.
Errorf
(
"unexpected error getting metadata.unknown: %v"
,
err
)
}
else
if
found
{
t
.
Errorf
(
"unexpected metadata.unknown=%#v: expected this to be pruned"
,
unknown
)
}
}
func
TestInvalidObjectMetaInStorage
(
t
*
testing
.
T
)
{
serverConfig
,
err
:=
testserver
.
DefaultServerConfig
()
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
stopCh
,
config
,
err
:=
testserver
.
StartServer
(
serverConfig
)
defer
close
(
stopCh
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
apiExtensionClient
,
err
:=
clientset
.
NewForConfig
(
config
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
dynamicClient
,
err
:=
dynamic
.
NewForConfig
(
config
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
noxuDefinition
:=
testserver
.
NewNoxuCustomResourceDefinition
(
apiextensionsv1beta1
.
NamespaceScoped
)
noxuDefinition
,
err
=
testserver
.
CreateNewCustomResourceDefinition
(
noxuDefinition
,
apiExtensionClient
,
dynamicClient
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
restOptions
,
err
:=
serverConfig
.
GenericConfig
.
RESTOptionsGetter
.
GetRESTOptions
(
schema
.
GroupResource
{
Group
:
noxuDefinition
.
Spec
.
Group
,
Resource
:
noxuDefinition
.
Spec
.
Names
.
Plural
})
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
tlsInfo
:=
transport
.
TLSInfo
{
CertFile
:
restOptions
.
StorageConfig
.
CertFile
,
KeyFile
:
restOptions
.
StorageConfig
.
KeyFile
,
CAFile
:
restOptions
.
StorageConfig
.
CAFile
,
}
tlsConfig
,
err
:=
tlsInfo
.
ClientConfig
()
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
etcdConfig
:=
clientv3
.
Config
{
Endpoints
:
restOptions
.
StorageConfig
.
ServerList
,
TLS
:
tlsConfig
,
}
etcdclient
,
err
:=
clientv3
.
New
(
etcdConfig
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
t
.
Logf
(
"Creating object with invalid labels manually in etcd"
)
original
:=
testserver
.
NewNoxuInstance
(
"default"
,
"foo"
)
unstructured
.
SetNestedField
(
original
.
UnstructuredContent
(),
int64
(
42
),
"metadata"
,
"unknown"
)
unstructured
.
SetNestedField
(
original
.
UnstructuredContent
(),
map
[
string
]
interface
{}{
"foo"
:
int64
(
42
),
"bar"
:
"abc"
},
"metadata"
,
"labels"
)
ctx
:=
genericapirequest
.
WithNamespace
(
genericapirequest
.
NewContext
(),
metav1
.
NamespaceDefault
)
key
:=
path
.
Join
(
"/"
,
restOptions
.
StorageConfig
.
Prefix
,
noxuDefinition
.
Spec
.
Group
,
"noxus/default/foo"
)
val
,
_
:=
json
.
Marshal
(
original
.
UnstructuredContent
())
if
_
,
err
:=
etcdclient
.
Put
(
ctx
,
key
,
string
(
val
));
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
t
.
Logf
(
"Checking that ObjectMeta is pruned from unknown fields"
)
noxuResourceClient
:=
newNamespacedCustomResourceClient
(
"default"
,
dynamicClient
,
noxuDefinition
)
obj
,
err
:=
noxuResourceClient
.
Get
(
"foo"
,
metav1
.
GetOptions
{})
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
if
unknown
,
found
,
err
:=
unstructured
.
NestedFieldNoCopy
(
obj
.
UnstructuredContent
(),
"metadata"
,
"unknown"
);
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
}
else
if
found
{
t
.
Errorf
(
"unexpected to find metadata.unknown=%#v"
,
unknown
)
}
t
.
Logf
(
"Checking that ObjectMeta is pruned from invalid typed fields"
)
if
labels
,
found
,
err
:=
unstructured
.
NestedStringMap
(
obj
.
UnstructuredContent
(),
"metadata"
,
"labels"
);
err
!=
nil
{
t
.
Errorf
(
"unexpected error: %v"
,
err
)
}
else
if
found
&&
!
reflect
.
DeepEqual
(
labels
,
map
[
string
]
string
{
"bar"
:
"abc"
})
{
t
.
Errorf
(
"unexpected to find metadata.lables=%#v"
,
labels
)
}
}
staging/src/k8s.io/apimachinery/pkg/apis/meta/fuzzer/fuzzer.go
View file @
1db0024e
...
@@ -181,16 +181,41 @@ func v1FuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} {
...
@@ -181,16 +181,41 @@ func v1FuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} {
j
.
Kind
=
""
j
.
Kind
=
""
},
},
func
(
j
*
metav1
.
ObjectMeta
,
c
fuzz
.
Continue
)
{
func
(
j
*
metav1
.
ObjectMeta
,
c
fuzz
.
Continue
)
{
j
.
Name
=
c
.
RandString
()
c
.
FuzzNoCustom
(
j
)
j
.
ResourceVersion
=
strconv
.
FormatUint
(
c
.
RandUint64
(),
10
)
j
.
ResourceVersion
=
strconv
.
FormatUint
(
c
.
RandUint64
(),
10
)
j
.
SelfLink
=
c
.
RandString
()
j
.
UID
=
types
.
UID
(
c
.
RandString
())
j
.
UID
=
types
.
UID
(
c
.
RandString
())
j
.
GenerateName
=
c
.
RandString
()
var
sec
,
nsec
int64
var
sec
,
nsec
int64
c
.
Fuzz
(
&
sec
)
c
.
Fuzz
(
&
sec
)
c
.
Fuzz
(
&
nsec
)
c
.
Fuzz
(
&
nsec
)
j
.
CreationTimestamp
=
metav1
.
Unix
(
sec
,
nsec
)
.
Rfc3339Copy
()
j
.
CreationTimestamp
=
metav1
.
Unix
(
sec
,
nsec
)
.
Rfc3339Copy
()
if
j
.
DeletionTimestamp
!=
nil
{
c
.
Fuzz
(
&
sec
)
c
.
Fuzz
(
&
nsec
)
t
:=
metav1
.
Unix
(
sec
,
nsec
)
.
Rfc3339Copy
()
j
.
DeletionTimestamp
=
&
t
}
if
len
(
j
.
Labels
)
==
0
{
j
.
Labels
=
nil
}
if
len
(
j
.
Annotations
)
==
0
{
j
.
Annotations
=
nil
}
if
len
(
j
.
OwnerReferences
)
==
0
{
j
.
OwnerReferences
=
nil
}
if
len
(
j
.
Finalizers
)
==
0
{
j
.
Finalizers
=
nil
}
},
func
(
j
*
metav1
.
Initializers
,
c
fuzz
.
Continue
)
{
c
.
FuzzNoCustom
(
j
)
if
len
(
j
.
Pending
)
==
0
{
j
.
Pending
=
nil
}
},
},
func
(
j
*
metav1
.
ListMeta
,
c
fuzz
.
Continue
)
{
func
(
j
*
metav1
.
ListMeta
,
c
fuzz
.
Continue
)
{
j
.
ResourceVersion
=
strconv
.
FormatUint
(
c
.
RandUint64
(),
10
)
j
.
ResourceVersion
=
strconv
.
FormatUint
(
c
.
RandUint64
(),
10
)
...
...
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