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 }