k8s.io/apiserver@v0.31.1/pkg/endpoints/handlers/fieldmanager/bench_test.go (about) 1 /* 2 Copyright 2023 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 fieldmanager_test 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "io/ioutil" 23 "path/filepath" 24 "strings" 25 "testing" 26 27 corev1 "k8s.io/api/core/v1" 28 "k8s.io/apimachinery/pkg/api/meta" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 31 "k8s.io/apimachinery/pkg/runtime" 32 "k8s.io/apimachinery/pkg/runtime/schema" 33 "k8s.io/apimachinery/pkg/runtime/serializer" 34 "k8s.io/apimachinery/pkg/util/managedfields" 35 "k8s.io/apimachinery/pkg/util/managedfields/managedfieldstest" 36 "k8s.io/kube-openapi/pkg/validation/spec" 37 "sigs.k8s.io/yaml" 38 ) 39 40 var fakeTypeConverter = func() managedfields.TypeConverter { 41 data, err := ioutil.ReadFile(filepath.Join(strings.Repeat(".."+string(filepath.Separator), 8), 42 "api", "openapi-spec", "swagger.json")) 43 if err != nil { 44 panic(err) 45 } 46 swagger := spec.Swagger{} 47 if err := json.Unmarshal(data, &swagger); err != nil { 48 panic(err) 49 } 50 definitions := map[string]*spec.Schema{} 51 for k, v := range swagger.Definitions { 52 p := v 53 definitions[k] = &p 54 } 55 typeConverter, err := managedfields.NewTypeConverter(definitions, false) 56 if err != nil { 57 panic(err) 58 } 59 return typeConverter 60 }() 61 62 func getObjectBytes(file string) []byte { 63 s, err := ioutil.ReadFile(file) 64 if err != nil { 65 panic(err) 66 } 67 return s 68 } 69 70 func BenchmarkNewObject(b *testing.B) { 71 tests := []struct { 72 gvk schema.GroupVersionKind 73 obj []byte 74 }{ 75 { 76 gvk: schema.FromAPIVersionAndKind("v1", "Pod"), 77 obj: getObjectBytes("pod.yaml"), 78 }, 79 { 80 gvk: schema.FromAPIVersionAndKind("v1", "Node"), 81 obj: getObjectBytes("node.yaml"), 82 }, 83 { 84 gvk: schema.FromAPIVersionAndKind("v1", "Endpoints"), 85 obj: getObjectBytes("endpoints.yaml"), 86 }, 87 } 88 scheme := runtime.NewScheme() 89 if err := corev1.AddToScheme(scheme); err != nil { 90 b.Fatalf("Failed to add to scheme: %v", err) 91 } 92 for _, test := range tests { 93 b.Run(test.gvk.Kind, func(b *testing.B) { 94 f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, test.gvk) 95 96 decoder := serializer.NewCodecFactory(scheme).UniversalDecoder(test.gvk.GroupVersion()) 97 newObj, err := runtime.Decode(decoder, test.obj) 98 if err != nil { 99 b.Fatalf("Failed to parse yaml object: %v", err) 100 } 101 objMeta, err := meta.Accessor(newObj) 102 if err != nil { 103 b.Fatalf("Failed to get object meta: %v", err) 104 } 105 objMeta.SetManagedFields([]metav1.ManagedFieldsEntry{ 106 { 107 Manager: "default", 108 Operation: "Update", 109 APIVersion: "v1", 110 }, 111 }) 112 appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} 113 if err := yaml.Unmarshal(test.obj, &appliedObj.Object); err != nil { 114 b.Fatalf("Failed to parse yaml object: %v", err) 115 } 116 b.Run("Update", func(b *testing.B) { 117 b.ReportAllocs() 118 b.ResetTimer() 119 for n := 0; n < b.N; n++ { 120 if err := f.Update(newObj, "fieldmanager_test"); err != nil { 121 b.Fatal(err) 122 } 123 f.Reset() 124 } 125 }) 126 b.Run("UpdateTwice", func(b *testing.B) { 127 b.ReportAllocs() 128 b.ResetTimer() 129 for n := 0; n < b.N; n++ { 130 if err := f.Update(newObj, "fieldmanager_test"); err != nil { 131 b.Fatal(err) 132 } 133 if err := f.Update(newObj, "fieldmanager_test_2"); err != nil { 134 b.Fatal(err) 135 } 136 f.Reset() 137 } 138 }) 139 b.Run("Apply", func(b *testing.B) { 140 b.ReportAllocs() 141 b.ResetTimer() 142 for n := 0; n < b.N; n++ { 143 if err := f.Apply(appliedObj, "fieldmanager_test", false); err != nil { 144 b.Fatal(err) 145 } 146 f.Reset() 147 } 148 }) 149 b.Run("UpdateApply", func(b *testing.B) { 150 b.ReportAllocs() 151 b.ResetTimer() 152 for n := 0; n < b.N; n++ { 153 if err := f.Update(newObj, "fieldmanager_test"); err != nil { 154 b.Fatal(err) 155 } 156 if err := f.Apply(appliedObj, "fieldmanager_test", false); err != nil { 157 b.Fatal(err) 158 } 159 f.Reset() 160 } 161 }) 162 }) 163 } 164 } 165 166 func toUnstructured(b *testing.B, o runtime.Object) *unstructured.Unstructured { 167 u, err := runtime.DefaultUnstructuredConverter.ToUnstructured(o) 168 if err != nil { 169 b.Fatalf("Failed to unmarshal to json: %v", err) 170 } 171 return &unstructured.Unstructured{Object: u} 172 } 173 174 func BenchmarkConvertObjectToTyped(b *testing.B) { 175 tests := []struct { 176 gvk schema.GroupVersionKind 177 obj []byte 178 }{ 179 { 180 gvk: schema.FromAPIVersionAndKind("v1", "Pod"), 181 obj: getObjectBytes("pod.yaml"), 182 }, 183 { 184 gvk: schema.FromAPIVersionAndKind("v1", "Node"), 185 obj: getObjectBytes("node.yaml"), 186 }, 187 { 188 gvk: schema.FromAPIVersionAndKind("v1", "Endpoints"), 189 obj: getObjectBytes("endpoints.yaml"), 190 }, 191 } 192 scheme := runtime.NewScheme() 193 if err := corev1.AddToScheme(scheme); err != nil { 194 b.Fatalf("Failed to add to scheme: %v", err) 195 } 196 197 for _, test := range tests { 198 b.Run(test.gvk.Kind, func(b *testing.B) { 199 decoder := serializer.NewCodecFactory(scheme).UniversalDecoder(test.gvk.GroupVersion()) 200 structured, err := runtime.Decode(decoder, test.obj) 201 if err != nil { 202 b.Fatalf("Failed to parse yaml object: %v", err) 203 } 204 b.Run("structured", func(b *testing.B) { 205 b.ReportAllocs() 206 b.RunParallel(func(pb *testing.PB) { 207 for pb.Next() { 208 _, err := fakeTypeConverter.ObjectToTyped(structured) 209 if err != nil { 210 b.Errorf("Error in ObjectToTyped: %v", err) 211 } 212 } 213 }) 214 }) 215 216 unstructured := toUnstructured(b, structured) 217 b.Run("unstructured", func(b *testing.B) { 218 b.ReportAllocs() 219 b.RunParallel(func(pb *testing.PB) { 220 for pb.Next() { 221 _, err := fakeTypeConverter.ObjectToTyped(unstructured) 222 if err != nil { 223 b.Errorf("Error in ObjectToTyped: %v", err) 224 } 225 } 226 }) 227 }) 228 }) 229 } 230 } 231 232 func BenchmarkCompare(b *testing.B) { 233 tests := []struct { 234 gvk schema.GroupVersionKind 235 obj []byte 236 }{ 237 { 238 gvk: schema.FromAPIVersionAndKind("v1", "Pod"), 239 obj: getObjectBytes("pod.yaml"), 240 }, 241 { 242 gvk: schema.FromAPIVersionAndKind("v1", "Node"), 243 obj: getObjectBytes("node.yaml"), 244 }, 245 { 246 gvk: schema.FromAPIVersionAndKind("v1", "Endpoints"), 247 obj: getObjectBytes("endpoints.yaml"), 248 }, 249 } 250 251 scheme := runtime.NewScheme() 252 if err := corev1.AddToScheme(scheme); err != nil { 253 b.Fatalf("Failed to add to scheme: %v", err) 254 } 255 256 for _, test := range tests { 257 b.Run(test.gvk.Kind, func(b *testing.B) { 258 decoder := serializer.NewCodecFactory(scheme).UniversalDecoder(test.gvk.GroupVersion()) 259 structured, err := runtime.Decode(decoder, test.obj) 260 if err != nil { 261 b.Fatal(err) 262 } 263 tv1, err := fakeTypeConverter.ObjectToTyped(structured) 264 if err != nil { 265 b.Errorf("Error in ObjectToTyped: %v", err) 266 } 267 tv2, err := fakeTypeConverter.ObjectToTyped(structured) 268 if err != nil { 269 b.Errorf("Error in ObjectToTyped: %v", err) 270 } 271 272 b.Run("structured", func(b *testing.B) { 273 b.ReportAllocs() 274 for n := 0; n < b.N; n++ { 275 _, err = tv1.Compare(tv2) 276 if err != nil { 277 b.Errorf("Error in ObjectToTyped: %v", err) 278 } 279 } 280 }) 281 282 unstructured := toUnstructured(b, structured) 283 utv1, err := fakeTypeConverter.ObjectToTyped(unstructured) 284 if err != nil { 285 b.Errorf("Error in ObjectToTyped: %v", err) 286 } 287 utv2, err := fakeTypeConverter.ObjectToTyped(unstructured) 288 if err != nil { 289 b.Errorf("Error in ObjectToTyped: %v", err) 290 } 291 b.Run("unstructured", func(b *testing.B) { 292 b.ReportAllocs() 293 b.RunParallel(func(pb *testing.PB) { 294 for pb.Next() { 295 _, err = utv1.Compare(utv2) 296 if err != nil { 297 b.Errorf("Error in ObjectToTyped: %v", err) 298 } 299 } 300 }) 301 }) 302 }) 303 } 304 } 305 306 func BenchmarkRepeatedUpdate(b *testing.B) { 307 f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) 308 podBytes := getObjectBytes("pod.yaml") 309 310 var obj *corev1.Pod 311 if err := yaml.Unmarshal(podBytes, &obj); err != nil { 312 b.Fatalf("Failed to parse yaml object: %v", err) 313 } 314 obj.Spec.Containers[0].Image = "nginx:latest" 315 objs := []*corev1.Pod{obj} 316 obj = obj.DeepCopy() 317 obj.Spec.Containers[0].Image = "nginx:4.3" 318 objs = append(objs, obj) 319 320 appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} 321 if err := yaml.Unmarshal(podBytes, &appliedObj.Object); err != nil { 322 b.Fatalf("error decoding YAML: %v", err) 323 } 324 325 err := f.Apply(appliedObj, "fieldmanager_apply", false) 326 if err != nil { 327 b.Fatal(err) 328 } 329 330 if err := f.Update(objs[1], "fieldmanager_1"); err != nil { 331 b.Fatal(err) 332 } 333 334 b.ReportAllocs() 335 b.ResetTimer() 336 for n := 0; n < b.N; n++ { 337 err := f.Update(objs[n%len(objs)], fmt.Sprintf("fieldmanager_%d", n%len(objs))) 338 if err != nil { 339 b.Fatal(err) 340 } 341 f.Reset() 342 } 343 }