k8s.io/kubernetes@v1.29.3/pkg/api/testing/unstructured_test.go (about) 1 /* 2 Copyright 2017 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 testing 18 19 import ( 20 "math/rand" 21 "reflect" 22 "testing" 23 24 "github.com/google/go-cmp/cmp" 25 fuzz "github.com/google/gofuzz" 26 27 v1 "k8s.io/api/core/v1" 28 "k8s.io/apimachinery/pkg/api/apitesting/fuzzer" 29 apiequality "k8s.io/apimachinery/pkg/api/equality" 30 "k8s.io/apimachinery/pkg/api/meta" 31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 32 metaunstruct "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 33 "k8s.io/apimachinery/pkg/runtime" 34 "k8s.io/apimachinery/pkg/runtime/schema" 35 "k8s.io/apimachinery/pkg/util/json" 36 "k8s.io/kubernetes/pkg/api/legacyscheme" 37 api "k8s.io/kubernetes/pkg/apis/core" 38 ) 39 40 func doRoundTrip(t *testing.T, internalVersion schema.GroupVersion, externalVersion schema.GroupVersion, kind string) { 41 // We do fuzzing on the internal version of the object, and only then 42 // convert to the external version. This is because custom fuzzing 43 // function are only supported for internal objects. 44 internalObj, err := legacyscheme.Scheme.New(internalVersion.WithKind(kind)) 45 if err != nil { 46 t.Fatalf("Couldn't create internal object %v: %v", kind, err) 47 } 48 seed := rand.Int63() 49 fuzzer.FuzzerFor(FuzzerFuncs, rand.NewSource(seed), legacyscheme.Codecs). 50 // We are explicitly overwriting custom fuzzing functions, to ensure 51 // that InitContainers and their statuses are not generated. This is 52 // because in this test we are simply doing json operations, in which 53 // those disappear. 54 Funcs( 55 func(s *api.PodSpec, c fuzz.Continue) { 56 c.FuzzNoCustom(s) 57 s.InitContainers = nil 58 }, 59 func(s *api.PodStatus, c fuzz.Continue) { 60 c.FuzzNoCustom(s) 61 s.InitContainerStatuses = nil 62 }, 63 ).Fuzz(internalObj) 64 65 item, err := legacyscheme.Scheme.New(externalVersion.WithKind(kind)) 66 if err != nil { 67 t.Fatalf("Couldn't create external object %v: %v", kind, err) 68 } 69 if err := legacyscheme.Scheme.Convert(internalObj, item, nil); err != nil { 70 t.Fatalf("Conversion for %v failed: %v", kind, err) 71 } 72 73 data, err := json.Marshal(item) 74 if err != nil { 75 t.Errorf("Error when marshaling object: %v", err) 76 return 77 } 78 unstr := make(map[string]interface{}) 79 err = json.Unmarshal(data, &unstr) 80 if err != nil { 81 t.Errorf("Error when unmarshaling to unstructured: %v", err) 82 return 83 } 84 85 data, err = json.Marshal(unstr) 86 if err != nil { 87 t.Errorf("Error when marshaling unstructured: %v", err) 88 return 89 } 90 unmarshalledObj := reflect.New(reflect.TypeOf(item).Elem()).Interface() 91 err = json.Unmarshal(data, &unmarshalledObj) 92 if err != nil { 93 t.Errorf("Error when unmarshaling to object: %v", err) 94 return 95 } 96 if !apiequality.Semantic.DeepEqual(item, unmarshalledObj) { 97 t.Errorf("Object changed during JSON operations, diff: %v", cmp.Diff(item, unmarshalledObj)) 98 return 99 } 100 101 newUnstr, err := runtime.DefaultUnstructuredConverter.ToUnstructured(item) 102 if err != nil { 103 t.Errorf("ToUnstructured failed: %v", err) 104 return 105 } 106 107 newObj := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object) 108 err = runtime.DefaultUnstructuredConverter.FromUnstructured(newUnstr, newObj) 109 if err != nil { 110 t.Errorf("FromUnstructured failed: %v", err) 111 return 112 } 113 114 if !apiequality.Semantic.DeepEqual(item, newObj) { 115 t.Errorf("Object changed, diff: %v", cmp.Diff(item, newObj)) 116 } 117 } 118 119 func TestRoundTrip(t *testing.T) { 120 for gvk := range legacyscheme.Scheme.AllKnownTypes() { 121 if nonRoundTrippableTypes.Has(gvk.Kind) { 122 continue 123 } 124 if gvk.Version == runtime.APIVersionInternal { 125 continue 126 } 127 t.Logf("Testing: %v in %v", gvk.Kind, gvk.GroupVersion().String()) 128 for i := 0; i < 50; i++ { 129 doRoundTrip(t, schema.GroupVersion{Group: gvk.Group, Version: runtime.APIVersionInternal}, gvk.GroupVersion(), gvk.Kind) 130 if t.Failed() { 131 break 132 } 133 } 134 } 135 } 136 137 func TestRoundTripWithEmptyCreationTimestamp(t *testing.T) { 138 for gvk := range legacyscheme.Scheme.AllKnownTypes() { 139 if nonRoundTrippableTypes.Has(gvk.Kind) { 140 continue 141 } 142 if gvk.Version == runtime.APIVersionInternal { 143 continue 144 } 145 146 item, err := legacyscheme.Scheme.New(gvk) 147 if err != nil { 148 t.Fatalf("Couldn't create external object %v: %v", gvk, err) 149 } 150 t.Logf("Testing: %v in %v", gvk.Kind, gvk.GroupVersion().String()) 151 152 unstrBody, err := runtime.DefaultUnstructuredConverter.ToUnstructured(item) 153 if err != nil { 154 t.Fatalf("ToUnstructured failed: %v", err) 155 } 156 157 unstructObj := &metaunstruct.Unstructured{} 158 unstructObj.Object = unstrBody 159 160 if meta, err := meta.Accessor(unstructObj); err == nil { 161 meta.SetCreationTimestamp(metav1.Time{}) 162 } else { 163 t.Fatalf("Unable to set creation timestamp: %v", err) 164 } 165 166 // attempt to re-convert unstructured object - conversion should not fail 167 // based on empty metadata fields, such as creationTimestamp 168 newObj := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object) 169 err = runtime.DefaultUnstructuredConverter.FromUnstructured(unstructObj.Object, newObj) 170 if err != nil { 171 t.Fatalf("FromUnstructured failed: %v", err) 172 } 173 } 174 } 175 176 func BenchmarkToUnstructured(b *testing.B) { 177 items := benchmarkItems(b) 178 size := len(items) 179 convertor := runtime.DefaultUnstructuredConverter 180 b.ResetTimer() 181 for i := 0; i < b.N; i++ { 182 unstr, err := convertor.ToUnstructured(&items[i%size]) 183 if err != nil || unstr == nil { 184 b.Fatalf("unexpected error: %v", err) 185 } 186 } 187 b.StopTimer() 188 } 189 190 func BenchmarkFromUnstructured(b *testing.B) { 191 items := benchmarkItems(b) 192 convertor := runtime.DefaultUnstructuredConverter 193 var unstr []map[string]interface{} 194 for i := range items { 195 item, err := convertor.ToUnstructured(&items[i]) 196 if err != nil || item == nil { 197 b.Fatalf("unexpected error: %v", err) 198 } 199 unstr = append(unstr, item) 200 } 201 size := len(items) 202 b.ResetTimer() 203 for i := 0; i < b.N; i++ { 204 obj := v1.Pod{} 205 if err := convertor.FromUnstructured(unstr[i%size], &obj); err != nil { 206 b.Fatalf("unexpected error: %v", err) 207 } 208 } 209 b.StopTimer() 210 } 211 212 func BenchmarkToUnstructuredViaJSON(b *testing.B) { 213 items := benchmarkItems(b) 214 var data [][]byte 215 for i := range items { 216 item, err := json.Marshal(&items[i]) 217 if err != nil { 218 b.Fatalf("unexpected error: %v", err) 219 } 220 data = append(data, item) 221 } 222 size := len(items) 223 b.ResetTimer() 224 for i := 0; i < b.N; i++ { 225 unstr := map[string]interface{}{} 226 if err := json.Unmarshal(data[i%size], &unstr); err != nil { 227 b.Fatalf("unexpected error: %v", err) 228 } 229 } 230 b.StopTimer() 231 } 232 233 func BenchmarkFromUnstructuredViaJSON(b *testing.B) { 234 items := benchmarkItems(b) 235 var unstr []map[string]interface{} 236 for i := range items { 237 data, err := json.Marshal(&items[i]) 238 if err != nil { 239 b.Fatalf("unexpected error: %v", err) 240 } 241 item := map[string]interface{}{} 242 if err := json.Unmarshal(data, &item); err != nil { 243 b.Fatalf("unexpected error: %v", err) 244 } 245 unstr = append(unstr, item) 246 } 247 size := len(items) 248 b.ResetTimer() 249 for i := 0; i < b.N; i++ { 250 item, err := json.Marshal(unstr[i%size]) 251 if err != nil { 252 b.Fatalf("unexpected error: %v", err) 253 } 254 obj := v1.Pod{} 255 if err := json.Unmarshal(item, &obj); err != nil { 256 b.Fatalf("unexpected error: %v", err) 257 } 258 } 259 b.StopTimer() 260 }