mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 03:41:45 +00:00
Merge pull request #70722 from yue9944882/test/enchance-test-regression-for-versioning-codec
Part of #68464: Enchance unit test regression for unstructured in versioning codec
This commit is contained in:
commit
d158952293
@ -8,12 +8,17 @@ load(
|
|||||||
|
|
||||||
go_test(
|
go_test(
|
||||||
name = "go_default_test",
|
name = "go_default_test",
|
||||||
srcs = ["versioning_test.go"],
|
srcs = [
|
||||||
|
"versioning_test.go",
|
||||||
|
"versioning_unstructured_test.go",
|
||||||
|
],
|
||||||
embed = [":go_default_library"],
|
embed = [":go_default_library"],
|
||||||
deps = [
|
deps = [
|
||||||
|
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||||
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||||
|
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -0,0 +1,338 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 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 versioning
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
)
|
||||||
|
|
||||||
|
func buildUnstructuredDecodable(gvk schema.GroupVersionKind) runtime.Object {
|
||||||
|
obj := &unstructured.Unstructured{}
|
||||||
|
obj.SetGroupVersionKind(gvk)
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildUnstructuredListDecodable(gvk schema.GroupVersionKind) runtime.Object {
|
||||||
|
obj := &unstructured.UnstructuredList{}
|
||||||
|
obj.SetGroupVersionKind(gvk)
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEncodeUnstructured(t *testing.T) {
|
||||||
|
v1GVK := schema.GroupVersionKind{
|
||||||
|
Group: "crispy",
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "Noxu",
|
||||||
|
}
|
||||||
|
v2GVK := schema.GroupVersionKind{
|
||||||
|
Group: "crispy",
|
||||||
|
Version: "v2",
|
||||||
|
Kind: "Noxu",
|
||||||
|
}
|
||||||
|
elseGVK := schema.GroupVersionKind{
|
||||||
|
Group: "crispy2",
|
||||||
|
Version: "else",
|
||||||
|
Kind: "Noxu",
|
||||||
|
}
|
||||||
|
elseUnstructuredDecodable := buildUnstructuredDecodable(elseGVK)
|
||||||
|
elseUnstructuredDecodableList := buildUnstructuredListDecodable(elseGVK)
|
||||||
|
v1UnstructuredDecodable := buildUnstructuredDecodable(v1GVK)
|
||||||
|
v1UnstructuredDecodableList := buildUnstructuredListDecodable(v1GVK)
|
||||||
|
v2UnstructuredDecodable := buildUnstructuredDecodable(v2GVK)
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
convertor runtime.ObjectConvertor
|
||||||
|
targetVersion runtime.GroupVersioner
|
||||||
|
outObj runtime.Object
|
||||||
|
typer runtime.ObjectTyper
|
||||||
|
|
||||||
|
errFunc func(error) bool
|
||||||
|
expectedObj runtime.Object
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "encode v1 unstructured with v2 encode version",
|
||||||
|
typer: &mockTyper{
|
||||||
|
gvks: []schema.GroupVersionKind{v1GVK},
|
||||||
|
},
|
||||||
|
outObj: v1UnstructuredDecodable,
|
||||||
|
targetVersion: v2GVK.GroupVersion(),
|
||||||
|
convertor: &checkConvertor{
|
||||||
|
obj: v2UnstructuredDecodable,
|
||||||
|
groupVersion: v2GVK.GroupVersion(),
|
||||||
|
},
|
||||||
|
expectedObj: v2UnstructuredDecodable,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "both typer and conversion are bypassed when unstructured gvk matches encode gvk",
|
||||||
|
typer: &mockTyper{
|
||||||
|
err: fmt.Errorf("unexpected typer call"),
|
||||||
|
},
|
||||||
|
outObj: v1UnstructuredDecodable,
|
||||||
|
targetVersion: v1GVK.GroupVersion(),
|
||||||
|
convertor: &checkConvertor{
|
||||||
|
err: fmt.Errorf("unexpected conversion happened"),
|
||||||
|
},
|
||||||
|
expectedObj: v1UnstructuredDecodable,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "encode will fail when unstructured object's gvk and encode gvk mismatches",
|
||||||
|
outObj: elseUnstructuredDecodable,
|
||||||
|
targetVersion: v1GVK.GroupVersion(),
|
||||||
|
errFunc: func(err error) bool {
|
||||||
|
return assert.Equal(t, runtime.NewNotRegisteredGVKErrForTarget("noxu-scheme", elseGVK, v1GVK.GroupVersion()), err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "encode with unstructured list's gvk regardless of its elements' gvk",
|
||||||
|
outObj: elseUnstructuredDecodableList,
|
||||||
|
targetVersion: elseGVK.GroupVersion(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "typer fail to recognize unstructured object gvk will fail the encoding",
|
||||||
|
outObj: elseUnstructuredDecodable,
|
||||||
|
targetVersion: v1GVK.GroupVersion(),
|
||||||
|
typer: &mockTyper{
|
||||||
|
err: fmt.Errorf("invalid obj gvk"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "encoding unstructured object without encode version will fallback to typer suggested version",
|
||||||
|
targetVersion: v1GVK.GroupVersion(),
|
||||||
|
convertor: &checkConvertor{
|
||||||
|
obj: v1UnstructuredDecodableList,
|
||||||
|
groupVersion: v1GVK.GroupVersion(),
|
||||||
|
},
|
||||||
|
outObj: elseUnstructuredDecodable,
|
||||||
|
typer: &mockTyper{
|
||||||
|
gvks: []schema.GroupVersionKind{v1GVK},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
serializer := &mockSerializer{}
|
||||||
|
codec := NewCodec(serializer, serializer, testCase.convertor, nil, testCase.typer, nil, testCase.targetVersion, nil, "noxu-scheme")
|
||||||
|
err := codec.Encode(testCase.outObj, ioutil.Discard)
|
||||||
|
if testCase.errFunc != nil {
|
||||||
|
if !testCase.errFunc(err) {
|
||||||
|
t.Errorf("%v: failed: %v", testCase.name, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, testCase.expectedObj, serializer.obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type errNotRecognizedGVK struct {
|
||||||
|
failedGVK schema.GroupVersionKind
|
||||||
|
claimingGVKs []schema.GroupVersionKind
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e errNotRecognizedGVK) Error() string {
|
||||||
|
return fmt.Sprintf("unrecognized gvk %v, should be one of %v", e.failedGVK, e.claimingGVKs)
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockUnstructuredNopConvertor struct {
|
||||||
|
claimingGVKs []schema.GroupVersionKind
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *mockUnstructuredNopConvertor) recognizeGVK(gvkToCheck schema.GroupVersionKind) error {
|
||||||
|
matched := false
|
||||||
|
for _, gvk := range c.claimingGVKs {
|
||||||
|
if gvk == gvkToCheck {
|
||||||
|
matched = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !matched {
|
||||||
|
return errNotRecognizedGVK{
|
||||||
|
failedGVK: gvkToCheck,
|
||||||
|
claimingGVKs: c.claimingGVKs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *mockUnstructuredNopConvertor) Convert(in, out, context interface{}) error {
|
||||||
|
inObj := in.(*unstructured.Unstructured)
|
||||||
|
outObj := out.(*unstructured.Unstructured)
|
||||||
|
if err := c.recognizeGVK(outObj.GroupVersionKind()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
outGVK := outObj.GetObjectKind().GroupVersionKind()
|
||||||
|
*outObj = *inObj.DeepCopy()
|
||||||
|
outObj.GetObjectKind().SetGroupVersionKind(outGVK)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *mockUnstructuredNopConvertor) ConvertToVersion(in runtime.Object, outVersion runtime.GroupVersioner) (runtime.Object, error) {
|
||||||
|
out := in.DeepCopyObject()
|
||||||
|
targetGVK, matched := outVersion.KindForGroupVersionKinds([]schema.GroupVersionKind{in.GetObjectKind().GroupVersionKind()})
|
||||||
|
if !matched {
|
||||||
|
return nil, fmt.Errorf("attempt to convert to mismatched gv %v", outVersion)
|
||||||
|
}
|
||||||
|
if err := c.recognizeGVK(out.GetObjectKind().GroupVersionKind()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out.GetObjectKind().SetGroupVersionKind(targetGVK)
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *mockUnstructuredNopConvertor) ConvertFieldLabel(gvk schema.GroupVersionKind, label, value string) (string, string, error) {
|
||||||
|
return "", "", fmt.Errorf("unexpected call to ConvertFieldLabel")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecodeUnstructured(t *testing.T) {
|
||||||
|
internalGVK := schema.GroupVersionKind{
|
||||||
|
Group: "crispy",
|
||||||
|
Version: runtime.APIVersionInternal,
|
||||||
|
Kind: "Noxu",
|
||||||
|
}
|
||||||
|
v1GVK := schema.GroupVersionKind{
|
||||||
|
Group: "crispy",
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "Noxu",
|
||||||
|
}
|
||||||
|
v2GVK := schema.GroupVersionKind{
|
||||||
|
Group: "crispy",
|
||||||
|
Version: "v2",
|
||||||
|
Kind: "Noxu",
|
||||||
|
}
|
||||||
|
internalUnstructuredDecodable := buildUnstructuredDecodable(internalGVK)
|
||||||
|
v1UnstructuredDecodable := buildUnstructuredDecodable(v1GVK)
|
||||||
|
v2UnstructuredDecodable := buildUnstructuredDecodable(v2GVK)
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
serializer runtime.Serializer
|
||||||
|
convertor runtime.ObjectConvertor
|
||||||
|
suggestedConvertVersion runtime.GroupVersioner
|
||||||
|
defaultGVK *schema.GroupVersionKind
|
||||||
|
intoObj runtime.Object
|
||||||
|
|
||||||
|
errFunc func(error) bool
|
||||||
|
expectedGVKOfSerializedData *schema.GroupVersionKind
|
||||||
|
expectedOut runtime.Object
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "decode v1 unstructured into non-nil v2 unstructured",
|
||||||
|
serializer: &mockSerializer{actual: &v1GVK, obj: v1UnstructuredDecodable},
|
||||||
|
convertor: &mockUnstructuredNopConvertor{
|
||||||
|
claimingGVKs: []schema.GroupVersionKind{
|
||||||
|
v1GVK, v2GVK,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
suggestedConvertVersion: v2GVK.GroupVersion(),
|
||||||
|
intoObj: v2UnstructuredDecodable,
|
||||||
|
expectedGVKOfSerializedData: &v1GVK,
|
||||||
|
expectedOut: v2UnstructuredDecodable,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "decode v1 unstructured into nil object with v2 version",
|
||||||
|
serializer: &mockSerializer{actual: &v1GVK, obj: v1UnstructuredDecodable},
|
||||||
|
convertor: &mockUnstructuredNopConvertor{
|
||||||
|
claimingGVKs: []schema.GroupVersionKind{
|
||||||
|
v1GVK, v2GVK,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
suggestedConvertVersion: v2GVK.GroupVersion(),
|
||||||
|
intoObj: nil,
|
||||||
|
expectedGVKOfSerializedData: &v1GVK,
|
||||||
|
expectedOut: v2UnstructuredDecodable,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "decode v1 unstructured into non-nil internal unstructured",
|
||||||
|
serializer: &mockSerializer{actual: &v1GVK, obj: v1UnstructuredDecodable},
|
||||||
|
convertor: &mockUnstructuredNopConvertor{
|
||||||
|
claimingGVKs: []schema.GroupVersionKind{
|
||||||
|
v1GVK, v2GVK,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
suggestedConvertVersion: internalGVK.GroupVersion(),
|
||||||
|
intoObj: internalUnstructuredDecodable,
|
||||||
|
errFunc: func(err error) bool {
|
||||||
|
notRecognized, ok := err.(errNotRecognizedGVK)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return assert.Equal(t, notRecognized.failedGVK, internalGVK)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "decode v1 unstructured into nil object with internal version",
|
||||||
|
serializer: &mockSerializer{actual: &v1GVK, obj: v1UnstructuredDecodable},
|
||||||
|
convertor: &mockUnstructuredNopConvertor{
|
||||||
|
claimingGVKs: []schema.GroupVersionKind{
|
||||||
|
v1GVK, v2GVK,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
suggestedConvertVersion: internalGVK.GroupVersion(),
|
||||||
|
intoObj: nil,
|
||||||
|
errFunc: func(err error) bool {
|
||||||
|
notRecognized, ok := err.(errNotRecognizedGVK)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return assert.Equal(t, notRecognized.failedGVK, internalGVK)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "skip conversion if serializer returns the same unstructured as into",
|
||||||
|
serializer: &mockSerializer{actual: &v1GVK, obj: v1UnstructuredDecodable},
|
||||||
|
convertor: &checkConvertor{
|
||||||
|
err: fmt.Errorf("unexpected conversion happened"),
|
||||||
|
},
|
||||||
|
suggestedConvertVersion: internalGVK.GroupVersion(),
|
||||||
|
intoObj: v1UnstructuredDecodable,
|
||||||
|
expectedGVKOfSerializedData: &v1GVK,
|
||||||
|
expectedOut: v1UnstructuredDecodable,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid convert version makes decoding unstructured fail",
|
||||||
|
serializer: &mockSerializer{actual: &v1GVK, obj: v1UnstructuredDecodable},
|
||||||
|
convertor: &checkConvertor{
|
||||||
|
in: v1UnstructuredDecodable,
|
||||||
|
groupVersion: internalGVK.GroupVersion(),
|
||||||
|
err: fmt.Errorf("no matching decode version"),
|
||||||
|
},
|
||||||
|
suggestedConvertVersion: internalGVK.GroupVersion(),
|
||||||
|
errFunc: func(err error) bool {
|
||||||
|
return assert.Equal(t, err, fmt.Errorf("no matching decode version"))
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
codec := NewCodec(testCase.serializer, testCase.serializer, testCase.convertor, nil, nil, nil, nil, testCase.suggestedConvertVersion, "noxu-scheme")
|
||||||
|
actualObj, actualSerializedGVK, err := codec.Decode([]byte(`{}`), testCase.defaultGVK, testCase.intoObj)
|
||||||
|
if testCase.errFunc != nil {
|
||||||
|
if !testCase.errFunc(err) {
|
||||||
|
t.Errorf("%v: failed: %v", testCase.name, err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, testCase.expectedOut, actualObj, "%v failed", testCase.name)
|
||||||
|
assert.Equal(t, testCase.expectedGVKOfSerializedData, actualSerializedGVK, "%v failed", testCase.name)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user