github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/runtime/serializer/versioning/versioning_unstructured_test.go (about)

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package versioning
    18  
    19  import (
    20  	"fmt"
    21  	"io/ioutil"
    22  	"testing"
    23  
    24  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1/unstructured"
    25  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime"
    26  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/schema"
    27  	"github.com/stretchr/testify/assert"
    28  )
    29  
    30  func buildUnstructuredDecodable(gvk schema.GroupVersionKind) runtime.Object {
    31  	obj := &unstructured.Unstructured{}
    32  	obj.SetGroupVersionKind(gvk)
    33  	return obj
    34  }
    35  
    36  func buildUnstructuredListDecodable(gvk schema.GroupVersionKind) runtime.Object {
    37  	obj := &unstructured.UnstructuredList{}
    38  	obj.SetGroupVersionKind(gvk)
    39  	return obj
    40  }
    41  
    42  func TestEncodeUnstructured(t *testing.T) {
    43  	v1GVK := schema.GroupVersionKind{
    44  		Group:   "crispy",
    45  		Version: "v1",
    46  		Kind:    "Noxu",
    47  	}
    48  	v2GVK := schema.GroupVersionKind{
    49  		Group:   "crispy",
    50  		Version: "v2",
    51  		Kind:    "Noxu",
    52  	}
    53  	elseGVK := schema.GroupVersionKind{
    54  		Group:   "crispy2",
    55  		Version: "else",
    56  		Kind:    "Noxu",
    57  	}
    58  	elseUnstructuredDecodable := buildUnstructuredDecodable(elseGVK)
    59  	elseUnstructuredDecodableList := buildUnstructuredListDecodable(elseGVK)
    60  	v1UnstructuredDecodable := buildUnstructuredDecodable(v1GVK)
    61  	v1UnstructuredDecodableList := buildUnstructuredListDecodable(v1GVK)
    62  	v2UnstructuredDecodable := buildUnstructuredDecodable(v2GVK)
    63  
    64  	testCases := []struct {
    65  		name          string
    66  		convertor     runtime.ObjectConvertor
    67  		targetVersion runtime.GroupVersioner
    68  		outObj        runtime.Object
    69  		typer         runtime.ObjectTyper
    70  
    71  		errFunc     func(error) bool
    72  		expectedObj runtime.Object
    73  	}{
    74  		{
    75  			name: "encode v1 unstructured with v2 encode version",
    76  			typer: &mockTyper{
    77  				gvks: []schema.GroupVersionKind{v1GVK},
    78  			},
    79  			outObj:        v1UnstructuredDecodable,
    80  			targetVersion: v2GVK.GroupVersion(),
    81  			convertor: &checkConvertor{
    82  				obj:          v2UnstructuredDecodable,
    83  				groupVersion: v2GVK.GroupVersion(),
    84  			},
    85  			expectedObj: v2UnstructuredDecodable,
    86  		},
    87  		{
    88  			name: "both typer and conversion are bypassed when unstructured gvk matches encode gvk",
    89  			typer: &mockTyper{
    90  				err: fmt.Errorf("unexpected typer call"),
    91  			},
    92  			outObj:        v1UnstructuredDecodable,
    93  			targetVersion: v1GVK.GroupVersion(),
    94  			convertor: &checkConvertor{
    95  				err: fmt.Errorf("unexpected conversion happened"),
    96  			},
    97  			expectedObj: v1UnstructuredDecodable,
    98  		},
    99  		{
   100  			name:          "encode will fail when unstructured object's gvk and encode gvk mismatches",
   101  			outObj:        elseUnstructuredDecodable,
   102  			targetVersion: v1GVK.GroupVersion(),
   103  			errFunc: func(err error) bool {
   104  				return assert.Equal(t, runtime.NewNotRegisteredGVKErrForTarget("noxu-scheme", elseGVK, v1GVK.GroupVersion()), err)
   105  			},
   106  		},
   107  		{
   108  			name:          "encode with unstructured list's gvk regardless of its elements' gvk",
   109  			outObj:        elseUnstructuredDecodableList,
   110  			targetVersion: elseGVK.GroupVersion(),
   111  		},
   112  		{
   113  			name:          "typer fail to recognize unstructured object gvk will fail the encoding",
   114  			outObj:        elseUnstructuredDecodable,
   115  			targetVersion: v1GVK.GroupVersion(),
   116  			typer: &mockTyper{
   117  				err: fmt.Errorf("invalid obj gvk"),
   118  			},
   119  		},
   120  		{
   121  			name:          "encoding unstructured object without encode version will fallback to typer suggested version",
   122  			targetVersion: v1GVK.GroupVersion(),
   123  			convertor: &checkConvertor{
   124  				obj:          v1UnstructuredDecodableList,
   125  				groupVersion: v1GVK.GroupVersion(),
   126  			},
   127  			outObj: elseUnstructuredDecodable,
   128  			typer: &mockTyper{
   129  				gvks: []schema.GroupVersionKind{v1GVK},
   130  			},
   131  		},
   132  	}
   133  	for _, testCase := range testCases {
   134  		serializer := &mockSerializer{}
   135  		codec := NewCodec(serializer, serializer, testCase.convertor, nil, testCase.typer, nil, testCase.targetVersion, nil, "noxu-scheme")
   136  		err := codec.Encode(testCase.outObj, ioutil.Discard)
   137  		if testCase.errFunc != nil {
   138  			if !testCase.errFunc(err) {
   139  				t.Errorf("%v: failed: %v", testCase.name, err)
   140  			}
   141  			return
   142  		}
   143  		assert.NoError(t, err)
   144  		assert.Equal(t, testCase.expectedObj, serializer.obj)
   145  	}
   146  }
   147  
   148  type errNotRecognizedGVK struct {
   149  	failedGVK    schema.GroupVersionKind
   150  	claimingGVKs []schema.GroupVersionKind
   151  }
   152  
   153  func (e errNotRecognizedGVK) Error() string {
   154  	return fmt.Sprintf("unrecognized gvk %v, should be one of %v", e.failedGVK, e.claimingGVKs)
   155  }
   156  
   157  type mockUnstructuredNopConvertor struct {
   158  	claimingGVKs []schema.GroupVersionKind
   159  }
   160  
   161  func (c *mockUnstructuredNopConvertor) recognizeGVK(gvkToCheck schema.GroupVersionKind) error {
   162  	matched := false
   163  	for _, gvk := range c.claimingGVKs {
   164  		if gvk == gvkToCheck {
   165  			matched = true
   166  		}
   167  	}
   168  	if !matched {
   169  		return errNotRecognizedGVK{
   170  			failedGVK:    gvkToCheck,
   171  			claimingGVKs: c.claimingGVKs,
   172  		}
   173  	}
   174  	return nil
   175  }
   176  
   177  func (c *mockUnstructuredNopConvertor) Convert(in, out, context interface{}) error {
   178  	inObj := in.(*unstructured.Unstructured)
   179  	outObj := out.(*unstructured.Unstructured)
   180  	if err := c.recognizeGVK(outObj.GroupVersionKind()); err != nil {
   181  		return err
   182  	}
   183  	outGVK := outObj.GetObjectKind().GroupVersionKind()
   184  	*outObj = *inObj.DeepCopy()
   185  	outObj.GetObjectKind().SetGroupVersionKind(outGVK)
   186  	return nil
   187  }
   188  
   189  func (c *mockUnstructuredNopConvertor) ConvertToVersion(in runtime.Object, outVersion runtime.GroupVersioner) (runtime.Object, error) {
   190  	out := in.DeepCopyObject()
   191  	targetGVK, matched := outVersion.KindForGroupVersionKinds([]schema.GroupVersionKind{in.GetObjectKind().GroupVersionKind()})
   192  	if !matched {
   193  		return nil, fmt.Errorf("attempt to convert to mismatched gv %v", outVersion)
   194  	}
   195  	if err := c.recognizeGVK(out.GetObjectKind().GroupVersionKind()); err != nil {
   196  		return nil, err
   197  	}
   198  	out.GetObjectKind().SetGroupVersionKind(targetGVK)
   199  	return out, nil
   200  }
   201  
   202  func (c *mockUnstructuredNopConvertor) ConvertFieldLabel(gvk schema.GroupVersionKind, label, value string) (string, string, error) {
   203  	return "", "", fmt.Errorf("unexpected call to ConvertFieldLabel")
   204  }
   205  
   206  func TestDecodeUnstructured(t *testing.T) {
   207  	internalGVK := schema.GroupVersionKind{
   208  		Group:   "crispy",
   209  		Version: runtime.APIVersionInternal,
   210  		Kind:    "Noxu",
   211  	}
   212  	v1GVK := schema.GroupVersionKind{
   213  		Group:   "crispy",
   214  		Version: "v1",
   215  		Kind:    "Noxu",
   216  	}
   217  	v2GVK := schema.GroupVersionKind{
   218  		Group:   "crispy",
   219  		Version: "v2",
   220  		Kind:    "Noxu",
   221  	}
   222  	internalUnstructuredDecodable := buildUnstructuredDecodable(internalGVK)
   223  	v1UnstructuredDecodable := buildUnstructuredDecodable(v1GVK)
   224  	v2UnstructuredDecodable := buildUnstructuredDecodable(v2GVK)
   225  
   226  	testCases := []struct {
   227  		name                    string
   228  		serializer              runtime.Serializer
   229  		convertor               runtime.ObjectConvertor
   230  		suggestedConvertVersion runtime.GroupVersioner
   231  		defaultGVK              *schema.GroupVersionKind
   232  		intoObj                 runtime.Object
   233  
   234  		errFunc                     func(error) bool
   235  		expectedGVKOfSerializedData *schema.GroupVersionKind
   236  		expectedOut                 runtime.Object
   237  	}{
   238  		{
   239  			name:       "decode v1 unstructured into non-nil v2 unstructured",
   240  			serializer: &mockSerializer{actual: &v1GVK, obj: v1UnstructuredDecodable},
   241  			convertor: &mockUnstructuredNopConvertor{
   242  				claimingGVKs: []schema.GroupVersionKind{
   243  					v1GVK, v2GVK,
   244  				},
   245  			},
   246  			suggestedConvertVersion:     v2GVK.GroupVersion(),
   247  			intoObj:                     v2UnstructuredDecodable,
   248  			expectedGVKOfSerializedData: &v1GVK,
   249  			expectedOut:                 v2UnstructuredDecodable,
   250  		},
   251  		{
   252  			name:       "decode v1 unstructured into nil object with v2 version",
   253  			serializer: &mockSerializer{actual: &v1GVK, obj: v1UnstructuredDecodable},
   254  			convertor: &mockUnstructuredNopConvertor{
   255  				claimingGVKs: []schema.GroupVersionKind{
   256  					v1GVK, v2GVK,
   257  				},
   258  			},
   259  			suggestedConvertVersion:     v2GVK.GroupVersion(),
   260  			intoObj:                     nil,
   261  			expectedGVKOfSerializedData: &v1GVK,
   262  			expectedOut:                 v2UnstructuredDecodable,
   263  		},
   264  		{
   265  			name:       "decode v1 unstructured into non-nil internal unstructured",
   266  			serializer: &mockSerializer{actual: &v1GVK, obj: v1UnstructuredDecodable},
   267  			convertor: &mockUnstructuredNopConvertor{
   268  				claimingGVKs: []schema.GroupVersionKind{
   269  					v1GVK, v2GVK,
   270  				},
   271  			},
   272  			suggestedConvertVersion: internalGVK.GroupVersion(),
   273  			intoObj:                 internalUnstructuredDecodable,
   274  			errFunc: func(err error) bool {
   275  				notRecognized, ok := err.(errNotRecognizedGVK)
   276  				if !ok {
   277  					return false
   278  				}
   279  				return assert.Equal(t, notRecognized.failedGVK, internalGVK)
   280  			},
   281  		},
   282  		{
   283  			name:       "decode v1 unstructured into nil object with internal version",
   284  			serializer: &mockSerializer{actual: &v1GVK, obj: v1UnstructuredDecodable},
   285  			convertor: &mockUnstructuredNopConvertor{
   286  				claimingGVKs: []schema.GroupVersionKind{
   287  					v1GVK, v2GVK,
   288  				},
   289  			},
   290  			suggestedConvertVersion: internalGVK.GroupVersion(),
   291  			intoObj:                 nil,
   292  			errFunc: func(err error) bool {
   293  				notRecognized, ok := err.(errNotRecognizedGVK)
   294  				if !ok {
   295  					return false
   296  				}
   297  				return assert.Equal(t, notRecognized.failedGVK, internalGVK)
   298  			},
   299  		},
   300  		{
   301  			name:       "skip conversion if serializer returns the same unstructured as into",
   302  			serializer: &mockSerializer{actual: &v1GVK, obj: v1UnstructuredDecodable},
   303  			convertor: &checkConvertor{
   304  				err: fmt.Errorf("unexpected conversion happened"),
   305  			},
   306  			suggestedConvertVersion:     internalGVK.GroupVersion(),
   307  			intoObj:                     v1UnstructuredDecodable,
   308  			expectedGVKOfSerializedData: &v1GVK,
   309  			expectedOut:                 v1UnstructuredDecodable,
   310  		},
   311  		{
   312  			name:       "invalid convert version makes decoding unstructured fail",
   313  			serializer: &mockSerializer{actual: &v1GVK, obj: v1UnstructuredDecodable},
   314  			convertor: &checkConvertor{
   315  				in:           v1UnstructuredDecodable,
   316  				groupVersion: internalGVK.GroupVersion(),
   317  				err:          fmt.Errorf("no matching decode version"),
   318  			},
   319  			suggestedConvertVersion: internalGVK.GroupVersion(),
   320  			errFunc: func(err error) bool {
   321  				return assert.Equal(t, err, fmt.Errorf("no matching decode version"))
   322  			},
   323  		},
   324  	}
   325  	for _, testCase := range testCases {
   326  		codec := NewCodec(testCase.serializer, testCase.serializer, testCase.convertor, nil, nil, nil, nil, testCase.suggestedConvertVersion, "noxu-scheme")
   327  		actualObj, actualSerializedGVK, err := codec.Decode([]byte(`{}`), testCase.defaultGVK, testCase.intoObj)
   328  		if testCase.errFunc != nil {
   329  			if !testCase.errFunc(err) {
   330  				t.Errorf("%v: failed: %v", testCase.name, err)
   331  			}
   332  			return
   333  		}
   334  		assert.NoError(t, err)
   335  		assert.Equal(t, testCase.expectedOut, actualObj, "%v failed", testCase.name)
   336  		assert.Equal(t, testCase.expectedGVKOfSerializedData, actualSerializedGVK, "%v failed", testCase.name)
   337  	}
   338  }