Skip to content

Commit

Permalink
Merge pull request #42944 from liggitt/patch-defaulting
Browse files Browse the repository at this point in the history
Automatic merge from submit-queue

Ensure patched objects are defaulted correctly

Restores defaulting behavior for patch API calls removed in e34e1ab#diff-517d1b81963bbc7c9b0a16e6eb3c0e2f

Restores the unit test that ensures we get a defaulted result after applying a patch

Fixes #42764
Fixes #42834
  • Loading branch information
Kubernetes Submit Queue committed Mar 12, 2017
2 parents 3f660a9 + 464db16 commit e315c38
Show file tree
Hide file tree
Showing 8 changed files with 45 additions and 16 deletions.
1 change: 1 addition & 0 deletions pkg/master/thirdparty/thirdparty.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ func (m *ThirdPartyResourceServer) thirdpartyapi(group, kind, version, pluralRes
Creater: thirdpartyresourcedata.NewObjectCreator(group, version, api.Scheme),
Convertor: api.Scheme,
Copier: api.Scheme,
Defaulter: api.Scheme,
Typer: api.Scheme,

Mapper: thirdpartyresourcedata.NewMapper(api.Registry.GroupOrDie(extensions.GroupName).RESTMapper, kind, version, group),
Expand Down
5 changes: 5 additions & 0 deletions staging/src/k8s.io/apiserver/pkg/endpoints/apiserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ func handleInternal(storage map[string]rest.Storage, admissionControl admission.
Creater: scheme,
Convertor: scheme,
Copier: scheme,
Defaulter: scheme,
Typer: scheme,
Linker: selfLinker,
Mapper: namespaceMapper,
Expand Down Expand Up @@ -2579,6 +2580,7 @@ func TestUpdateREST(t *testing.T) {
Creater: scheme,
Convertor: scheme,
Copier: scheme,
Defaulter: scheme,
Typer: scheme,
Linker: selfLinker,

Expand Down Expand Up @@ -2663,6 +2665,7 @@ func TestParentResourceIsRequired(t *testing.T) {
Creater: scheme,
Convertor: scheme,
Copier: scheme,
Defaulter: scheme,
Typer: scheme,
Linker: selfLinker,

Expand Down Expand Up @@ -2694,6 +2697,7 @@ func TestParentResourceIsRequired(t *testing.T) {
Creater: scheme,
Convertor: scheme,
Copier: scheme,
Defaulter: scheme,
Typer: scheme,
Linker: selfLinker,

Expand Down Expand Up @@ -3307,6 +3311,7 @@ func TestXGSubresource(t *testing.T) {
Creater: scheme,
Convertor: scheme,
Copier: scheme,
Defaulter: scheme,
Typer: scheme,
Linker: selfLinker,
Mapper: namespaceMapper,
Expand Down
1 change: 1 addition & 0 deletions staging/src/k8s.io/apiserver/pkg/endpoints/groupversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ type APIGroupVersion struct {
Creater runtime.ObjectCreater
Convertor runtime.ObjectConvertor
Copier runtime.ObjectCopier
Defaulter runtime.ObjectDefaulter
Linker runtime.SelfLinker
UnsafeConvertor runtime.ObjectConvertor

Expand Down
17 changes: 13 additions & 4 deletions staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ func patchObjectJSON(
// NOTE: Both <originalObject> and <objToUpdate> are supposed to be versioned.
func strategicPatchObject(
codec runtime.Codec,
defaulter runtime.ObjectDefaulter,
originalObject runtime.Object,
patchJS []byte,
objToUpdate runtime.Object,
Expand All @@ -96,17 +97,18 @@ func strategicPatchObject(
return nil, nil, err
}

if err := applyPatchToObject(codec, originalObjMap, patchMap, objToUpdate, versionedObj); err != nil {
if err := applyPatchToObject(codec, defaulter, originalObjMap, patchMap, objToUpdate, versionedObj); err != nil {
return nil, nil, err
}
return
}

// applyPatchToObject applies a strategic merge patch of <patchMap> to
// <originalMap> and stores the result in <objToUpdate>, though it operates
// on versioned map[string]interface{} representations.
// <originalMap> and stores the result in <objToUpdate>.
// NOTE: <objToUpdate> must be a versioned object.
func applyPatchToObject(
codec runtime.Codec,
defaulter runtime.ObjectDefaulter,
originalMap map[string]interface{},
patchMap map[string]interface{},
objToUpdate runtime.Object,
Expand All @@ -117,5 +119,12 @@ func applyPatchToObject(
return err
}

return unstructured.DefaultConverter.FromUnstructured(patchedObjMap, objToUpdate)
// Rather than serialize the patched map to JSON, then decode it to an object, we go directly from a map to an object
if err := unstructured.DefaultConverter.FromUnstructured(patchedObjMap, objToUpdate); err != nil {
return err
}
// Decoding from JSON to a versioned object would apply defaults, so we do the same here
defaulter.Default(objToUpdate)

return nil
}
8 changes: 5 additions & 3 deletions staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ type RequestScope struct {

Creater runtime.ObjectCreater
Convertor runtime.ObjectConvertor
Defaulter runtime.ObjectDefaulter
Copier runtime.ObjectCopier
Typer runtime.ObjectTyper
UnsafeConvertor runtime.ObjectConvertor
Expand Down Expand Up @@ -525,7 +526,7 @@ func PatchResource(r rest.Patcher, scope RequestScope, admit admission.Interface
}

result, err := patchResource(ctx, updateAdmit, timeout, versionedObj, r, name, patchType, patchJS,
scope.Namer, scope.Copier, scope.Creater, scope.UnsafeConvertor, scope.Kind, scope.Resource, codec)
scope.Namer, scope.Copier, scope.Creater, scope.Defaulter, scope.UnsafeConvertor, scope.Kind, scope.Resource, codec)
if err != nil {
scope.err(err, res.ResponseWriter, req.Request)
return
Expand Down Expand Up @@ -556,6 +557,7 @@ func patchResource(
namer ScopeNamer,
copier runtime.ObjectCopier,
creater runtime.ObjectCreater,
defaulter runtime.ObjectDefaulter,
unsafeConvertor runtime.ObjectConvertor,
kind schema.GroupVersionKind,
resource schema.GroupVersionResource,
Expand Down Expand Up @@ -619,7 +621,7 @@ func patchResource(
if err != nil {
return nil, err
}
originalMap, patchMap, err := strategicPatchObject(codec, currentVersionedObject, patchJS, versionedObjToUpdate, versionedObj)
originalMap, patchMap, err := strategicPatchObject(codec, defaulter, currentVersionedObject, patchJS, versionedObjToUpdate, versionedObj)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -716,7 +718,7 @@ func patchResource(
if err != nil {
return nil, err
}
if err := applyPatchToObject(codec, currentObjMap, originalPatchMap, versionedObjToUpdate, versionedObj); err != nil {
if err := applyPatchToObject(codec, defaulter, currentObjMap, originalPatchMap, versionedObjToUpdate, versionedObj); err != nil {
return nil, err
}
// Convert the object back to unversioned.
Expand Down
27 changes: 18 additions & 9 deletions staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ func TestPatchAnonymousField(t *testing.T) {
testGV := schema.GroupVersion{Group: "", Version: "v"}
api.Scheme.AddKnownTypes(testGV, &testPatchType{})
codec := api.Codecs.LegacyCodec(testGV)
defaulter := runtime.ObjectDefaulter(api.Scheme)

original := &testPatchType{
TypeMeta: metav1.TypeMeta{Kind: "testPatchType", APIVersion: "v"},
Expand All @@ -73,7 +74,7 @@ func TestPatchAnonymousField(t *testing.T) {
}

actual := &testPatchType{}
_, _, err := strategicPatchObject(codec, original, []byte(patch), actual, &testPatchType{})
_, _, err := strategicPatchObject(codec, defaulter, original, []byte(patch), actual, &testPatchType{})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
Expand Down Expand Up @@ -203,6 +204,7 @@ func (tc *patchTestCase) Run(t *testing.T) {
namer := &testNamer{namespace, name}
copier := runtime.ObjectCopier(api.Scheme)
creater := runtime.ObjectCreater(api.Scheme)
defaulter := runtime.ObjectDefaulter(api.Scheme)
convertor := runtime.UnsafeObjectConvertor(api.Scheme)
kind := schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Pod"}
resource := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"}
Expand Down Expand Up @@ -247,7 +249,7 @@ func (tc *patchTestCase) Run(t *testing.T) {

}

resultObj, err := patchResource(ctx, admit, 1*time.Second, versionedObj, testPatcher, name, patchType, patch, namer, copier, creater, convertor, kind, resource, codec)
resultObj, err := patchResource(ctx, admit, 1*time.Second, versionedObj, testPatcher, name, patchType, patch, namer, copier, creater, defaulter, convertor, kind, resource, codec)
if len(tc.expectedError) != 0 {
if err == nil || err.Error() != tc.expectedError {
t.Errorf("%s: expected error %v, but got %v", tc.name, tc.expectedError, err)
Expand All @@ -269,12 +271,18 @@ func (tc *patchTestCase) Run(t *testing.T) {

resultPod := resultObj.(*api.Pod)

// TODO: In the process of applying patches, we are calling
// conversions between versioned and unversioned objects.
// In case of Pod, conversion from versioned to unversioned
// is setting PodSecurityContext, so we need to set it here too.
reallyExpectedPod := tc.expectedPod
reallyExpectedPod.Spec.SecurityContext = &api.PodSecurityContext{}
// roundtrip to get defaulting
expectedJS, err := runtime.Encode(codec, tc.expectedPod)
if err != nil {
t.Errorf("%s: unexpected error: %v", tc.name, err)
return
}
expectedObj, err := runtime.Decode(codec, expectedJS)
if err != nil {
t.Errorf("%s: unexpected error: %v", tc.name, err)
return
}
reallyExpectedPod := expectedObj.(*api.Pod)

if !reflect.DeepEqual(*reallyExpectedPod, *resultPod) {
t.Errorf("%s mismatch: %v\n", tc.name, diff.ObjectGoPrintDiff(reallyExpectedPod, resultPod))
Expand All @@ -286,6 +294,7 @@ func (tc *patchTestCase) Run(t *testing.T) {

func TestNumberConversion(t *testing.T) {
codec := api.Codecs.LegacyCodec(schema.GroupVersion{Version: "v1"})
defaulter := runtime.ObjectDefaulter(api.Scheme)

currentVersionedObject := &v1.Service{
TypeMeta: metav1.TypeMeta{Kind: "Service", APIVersion: "v1"},
Expand All @@ -305,7 +314,7 @@ func TestNumberConversion(t *testing.T) {

patchJS := []byte(`{"spec":{"ports":[{"port":80,"nodePort":31789}]}}`)

_, _, err := strategicPatchObject(codec, currentVersionedObject, patchJS, versionedObjToUpdate, versionedObj)
_, _, err := strategicPatchObject(codec, defaulter, currentVersionedObject, patchJS, versionedObjToUpdate, versionedObj)
if err != nil {
t.Fatal(err)
}
Expand Down
1 change: 1 addition & 0 deletions staging/src/k8s.io/apiserver/pkg/endpoints/installer.go
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
Creater: a.group.Creater,
Convertor: a.group.Convertor,
Copier: a.group.Copier,
Defaulter: a.group.Defaulter,
Typer: a.group.Typer,
UnsafeConvertor: a.group.UnsafeConvertor,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ func (s *GenericAPIServer) newAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupV
Convertor: apiGroupInfo.Scheme,
UnsafeConvertor: runtime.UnsafeObjectConvertor(apiGroupInfo.Scheme),
Copier: apiGroupInfo.Scheme,
Defaulter: apiGroupInfo.Scheme,
Typer: apiGroupInfo.Scheme,
SubresourceGroupVersionKind: apiGroupInfo.SubresourceGroupVersionKind,
Linker: apiGroupInfo.GroupMeta.SelfLinker,
Expand Down

0 comments on commit e315c38

Please sign in to comment.