Commit 366b3829 authored by Alex Mohr's avatar Alex Mohr

Merge pull request #10200 from caesarxuchao/resthandler-validate-version

verify and default APIVersion in createHandler, verify APIVersion in UpdateResource
parents ad4086ea 664d20c5
......@@ -37,6 +37,10 @@ func (fakeCodec) DecodeInto([]byte, runtime.Object) error {
return nil
}
func (fakeCodec) DecodeIntoWithSpecifiedVersionKind([]byte, runtime.Object, string, string) error {
return nil
}
type fakeConvertor struct{}
func (fakeConvertor) Convert(in, out interface{}) error {
......
......@@ -302,7 +302,7 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object
}
obj := r.New()
if err := scope.Codec.DecodeInto(body, obj); err != nil {
if err := scope.Codec.DecodeIntoWithSpecifiedVersionKind(body, obj, scope.APIVersion, scope.Kind); err != nil {
err = transformDecodeError(typer, err, obj, body)
errorJSON(err, scope.Codec, w)
return
......@@ -469,7 +469,7 @@ func UpdateResource(r rest.Updater, scope RequestScope, typer runtime.ObjectType
}
obj := r.New()
if err := scope.Codec.DecodeInto(body, obj); err != nil {
if err := scope.Codec.DecodeIntoWithSpecifiedVersionKind(body, obj, scope.APIVersion, scope.Kind); err != nil {
err = transformDecodeError(typer, err, obj, body)
errorJSON(err, scope.Codec, w)
return
......
......@@ -80,6 +80,17 @@ func (s *Scheme) Decode(data []byte) (interface{}, error) {
// If obj's version doesn't match that in data, an attempt will be made to convert
// data into obj's version.
func (s *Scheme) DecodeInto(data []byte, obj interface{}) error {
return s.DecodeIntoWithSpecifiedVersionKind(data, obj, "", "")
}
// DecodeIntoWithSpecifiedVersionKind compares the passed in specifiedVersion and
// specifiedKind with data.Version and data.Kind, defaulting data.Version and
// data.Kind to the specified value if they are empty, or generating an error if
// data.Version and data.Kind are not empty and differ from the specified value.
// The function then implements the functionality of DecodeInto.
// If specifiedVersion and specifiedKind are empty, the function degenerates to
// DecodeInto.
func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj interface{}, specifiedVersion, specifiedKind string) error {
if len(data) == 0 {
return errors.New("empty input")
}
......@@ -87,6 +98,19 @@ func (s *Scheme) DecodeInto(data []byte, obj interface{}) error {
if err != nil {
return err
}
if dataVersion == "" {
dataVersion = specifiedVersion
}
if dataKind == "" {
dataKind = specifiedKind
}
if len(specifiedVersion) > 0 && (dataVersion != specifiedVersion) {
return errors.New(fmt.Sprintf("The apiVersion in the data (%s) does not match the specified apiVersion(%s)", dataVersion, specifiedVersion))
}
if len(specifiedKind) > 0 && (dataKind != specifiedKind) {
return errors.New(fmt.Sprintf("The kind in the data (%s) does not match the specified kind(%s)", dataKind, specifiedKind))
}
objVersion, objKind, err := s.ObjectVersionAndKind(obj)
if err != nil {
return err
......
......@@ -38,6 +38,7 @@ type ObjectCodec interface {
type Decoder interface {
Decode(data []byte) (Object, error)
DecodeInto(data []byte, obj Object) error
DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, kind, version string) error
}
// Encoder defines methods for serializing API objects into bytes
......
......@@ -458,6 +458,10 @@ func (s *Scheme) DecodeInto(data []byte, obj Object) error {
return s.raw.DecodeInto(data, obj)
}
func (s *Scheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, version, kind string) error {
return s.raw.DecodeIntoWithSpecifiedVersionKind(data, obj, version, kind)
}
// Copy does a deep copy of an API object. Useful mostly for tests.
func (s *Scheme) Copy(src Object) (Object, error) {
dst, err := s.raw.DeepCopy(src)
......
......@@ -74,6 +74,10 @@ func (unstructuredJSONScheme) DecodeInto(data []byte, obj Object) error {
return nil
}
func (unstructuredJSONScheme) DecodeIntoWithSpecifiedVersionKind(data []byte, obj Object, kind, version string) error {
return nil
}
func (unstructuredJSONScheme) DataVersionAndKind(data []byte) (version, kind string, err error) {
obj := TypeMeta{}
if err := json.Unmarshal(data, &obj); err != nil {
......
......@@ -51,12 +51,6 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/test/integration/framework"
)
var nodeResourceName string
func init() {
nodeResourceName = "nodes"
}
const (
AliceToken string = "abc123" // username: alice. Present in token file.
BobToken string = "xyz987" // username: bob. Present in token file.
......@@ -86,7 +80,7 @@ func timeoutPath(resource, namespace, name string) string {
var aPod string = `
{
"kind": "Pod",
"apiVersion": "v1",
"apiVersion": "` + testapi.Version() + `",
"metadata": {
"name": "a",
"creationTimestamp": null%s
......@@ -104,7 +98,7 @@ var aPod string = `
var aRC string = `
{
"kind": "ReplicationController",
"apiVersion": "v1",
"apiVersion": "` + testapi.Version() + `",
"metadata": {
"name": "a",
"labels": {
......@@ -137,7 +131,7 @@ var aRC string = `
var aService string = `
{
"kind": "Service",
"apiVersion": "v1",
"apiVersion": "` + testapi.Version() + `",
"metadata": {
"name": "a",
"labels": {
......@@ -161,7 +155,7 @@ var aService string = `
var aNode string = `
{
"kind": "Node",
"apiVersion": "v1",
"apiVersion": "` + testapi.Version() + `",
"metadata": {
"name": "a"%s
},
......@@ -173,7 +167,7 @@ var aNode string = `
var aEvent string = `
{
"kind": "Event",
"apiVersion": "v1",
"apiVersion": "` + testapi.Version() + `",
"metadata": {
"name": "a"%s
},
......@@ -189,7 +183,7 @@ var aEvent string = `
var aBinding string = `
{
"kind": "Binding",
"apiVersion": "v1",
"apiVersion": "` + testapi.Version() + `",
"metadata": {
"name": "a"%s
},
......@@ -212,7 +206,7 @@ var emptyEndpoints string = `
var aEndpoints string = `
{
"kind": "Endpoints",
"apiVersion": "v1",
"apiVersion": "` + testapi.Version() + `",
"metadata": {
"name": "a"%s
},
......@@ -237,7 +231,7 @@ var aEndpoints string = `
var deleteNow string = `
{
"kind": "DeleteOptions",
"apiVersion": "v1",
"apiVersion": "` + testapi.Version() + `",
"gracePeriodSeconds": null%s
}
`
......@@ -337,11 +331,11 @@ func getTestRequests() []struct {
{"DELETE", timeoutPath("endpoints", api.NamespaceDefault, "a"), "", code200},
// Normal methods on minions
{"GET", path(nodeResourceName, "", ""), "", code200},
{"POST", timeoutPath(nodeResourceName, "", ""), aNode, code201},
{"PUT", timeoutPath(nodeResourceName, "", "a"), aNode, code200},
{"GET", path(nodeResourceName, "", "a"), "", code200},
{"DELETE", timeoutPath(nodeResourceName, "", "a"), "", code200},
{"GET", path("nodes", "", ""), "", code200},
{"POST", timeoutPath("nodes", "", ""), aNode, code201},
{"PUT", timeoutPath("nodes", "", "a"), aNode, code200},
{"GET", path("nodes", "", "a"), "", code200},
{"DELETE", timeoutPath("nodes", "", "a"), "", code200},
// Normal methods on events
{"GET", path("events", "", ""), "", code200},
......@@ -367,8 +361,8 @@ func getTestRequests() []struct {
{"DELETE", timeoutPath("foo", api.NamespaceDefault, ""), "", code404},
// Special verbs on nodes
{"GET", pathWithPrefix("proxy", nodeResourceName, api.NamespaceDefault, "a"), "", code404},
{"GET", pathWithPrefix("redirect", nodeResourceName, api.NamespaceDefault, "a"), "", code404},
{"GET", pathWithPrefix("proxy", "nodes", api.NamespaceDefault, "a"), "", code404},
{"GET", pathWithPrefix("redirect", "nodes", api.NamespaceDefault, "a"), "", code404},
// TODO: test .../watch/..., which doesn't end before the test timeout.
// TODO: figure out how to create a minion so that it can successfully proxy/redirect.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment