From c4ca26f84d0f0b6d98eb109294491649981e10c4 Mon Sep 17 00:00:00 2001 From: deads2k Date: Fri, 2 Oct 2015 11:30:44 -0400 Subject: [PATCH] fix patch for anonymous struct fields --- pkg/apiserver/resthandler_test.go | 51 +++++++++++++++++++++++++++++++ third_party/forked/json/fields.go | 16 ++++++++-- 2 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 pkg/apiserver/resthandler_test.go diff --git a/pkg/apiserver/resthandler_test.go b/pkg/apiserver/resthandler_test.go new file mode 100644 index 00000000000..3249271249b --- /dev/null +++ b/pkg/apiserver/resthandler_test.go @@ -0,0 +1,51 @@ +/* +Copyright 2014 The Kubernetes Authors All rights reserved. + +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 ( + "testing" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" +) + +type testPatchType struct { + unversioned.TypeMeta `json:",inline"` + + testPatchSubType `json:",inline"` +} + +type testPatchSubType struct { + StringField string `json:"theField"` +} + +func (*testPatchType) IsAnAPIObject() {} + +func TestPatchAnonymousField(t *testing.T) { + originalJS := `{"kind":"testPatchType","theField":"my-value"}` + patch := `{"theField": "changed!"}` + expectedJS := `{"kind":"testPatchType","theField":"changed!"}` + + actualBytes, err := getPatchedJS(string(api.StrategicMergePatchType), []byte(originalJS), []byte(patch), &testPatchType{}) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if string(actualBytes) != expectedJS { + t.Errorf("expected %v, got %v", expectedJS, string(actualBytes)) + } + +} diff --git a/third_party/forked/json/fields.go b/third_party/forked/json/fields.go index 6384f91104a..1d17270ee46 100644 --- a/third_party/forked/json/fields.go +++ b/third_party/forked/json/fields.go @@ -44,9 +44,12 @@ func LookupPatchMetadata(t reflect.Type, jsonField string) (reflect.Type, string } } if f != nil { - // Find the reflect.Value of the most preferential - // struct field. + // Find the reflect.Value of the most preferential struct field. tjf := t.Field(f.index[0]) + // we must navigate down all the anonymously included structs in the chain + for i := 1; i < len(f.index); i++ { + tjf = tjf.Type.Field(f.index[i]) + } patchStrategy := tjf.Tag.Get("patchStrategy") patchMergeKey := tjf.Tag.Get("patchMergeKey") return tjf.Type, patchStrategy, patchMergeKey, nil @@ -60,13 +63,20 @@ type field struct { nameBytes []byte // []byte(name) equalFold func(s, t []byte) bool // bytes.EqualFold or equivalent - tag bool + tag bool + // index is the sequence of indexes from the containing type fields to this field. + // it is a slice because anonymous structs will need multiple navigation steps to correctly + // resolve the proper fields index []int typ reflect.Type omitEmpty bool quoted bool } +func (f field) String() string { + return fmt.Sprintf("{name: %s, type: %v, tag: %v, index: %v, omitEmpty: %v, quoted: %v}", f.name, f.typ, f.tag, f.index, f.omitEmpty, f.quoted) +} + func fillField(f field) field { f.nameBytes = []byte(f.name) f.equalFold = foldFunc(f.nameBytes)