k8s.io/apimachinery@v0.29.2/pkg/util/managedfields/scalehandler_test.go (about) 1 /* 2 Copyright 2021 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 managedfields 18 19 import ( 20 "reflect" 21 "testing" 22 "time" 23 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/apimachinery/pkg/runtime/schema" 26 "sigs.k8s.io/structured-merge-diff/v4/fieldpath" 27 ) 28 29 func TestTransformManagedFieldsToSubresource(t *testing.T) { 30 testTime, _ := time.ParseInLocation("2006-Jan-02", "2013-Feb-03", time.Local) 31 managedFieldTime := metav1.NewTime(testTime) 32 33 tests := []struct { 34 desc string 35 input []metav1.ManagedFieldsEntry 36 expected []metav1.ManagedFieldsEntry 37 }{ 38 { 39 desc: "filter one entry and transform it into a subresource entry", 40 input: []metav1.ManagedFieldsEntry{ 41 { 42 Manager: "manager-1", 43 Operation: metav1.ManagedFieldsOperationApply, 44 APIVersion: "apps/v1", 45 FieldsType: "FieldsV1", 46 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:another-field":{}}}`)}, 47 }, 48 { 49 Manager: "manager-2", 50 Operation: metav1.ManagedFieldsOperationApply, 51 APIVersion: "apps/v1", 52 FieldsType: "FieldsV1", 53 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 54 Time: &managedFieldTime, 55 }, 56 }, 57 expected: []metav1.ManagedFieldsEntry{ 58 { 59 Manager: "manager-2", 60 Operation: metav1.ManagedFieldsOperationApply, 61 APIVersion: "autoscaling/v1", 62 FieldsType: "FieldsV1", 63 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 64 Time: &managedFieldTime, 65 }, 66 }, 67 }, 68 { 69 desc: "transform all entries", 70 input: []metav1.ManagedFieldsEntry{ 71 { 72 Manager: "manager-1", 73 Operation: metav1.ManagedFieldsOperationApply, 74 APIVersion: "apps/v1", 75 FieldsType: "FieldsV1", 76 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 77 }, 78 { 79 Manager: "manager-2", 80 Operation: metav1.ManagedFieldsOperationApply, 81 APIVersion: "apps/v1", 82 FieldsType: "FieldsV1", 83 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 84 }, 85 { 86 Manager: "manager-3", 87 Operation: metav1.ManagedFieldsOperationApply, 88 APIVersion: "apps/v1", 89 FieldsType: "FieldsV1", 90 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 91 Subresource: "scale", 92 }, 93 }, 94 expected: []metav1.ManagedFieldsEntry{ 95 { 96 Manager: "manager-1", 97 Operation: metav1.ManagedFieldsOperationApply, 98 APIVersion: "autoscaling/v1", 99 FieldsType: "FieldsV1", 100 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 101 }, 102 { 103 Manager: "manager-2", 104 Operation: metav1.ManagedFieldsOperationApply, 105 APIVersion: "autoscaling/v1", 106 FieldsType: "FieldsV1", 107 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 108 }, 109 { 110 Manager: "manager-3", 111 Operation: metav1.ManagedFieldsOperationApply, 112 APIVersion: "autoscaling/v1", 113 FieldsType: "FieldsV1", 114 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 115 Subresource: "scale", 116 }, 117 }, 118 }, 119 { 120 desc: "drops fields if the api version is unknown", 121 input: []metav1.ManagedFieldsEntry{ 122 { 123 Manager: "manager-1", 124 Operation: metav1.ManagedFieldsOperationApply, 125 APIVersion: "apps/v10", 126 FieldsType: "FieldsV1", 127 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 128 }, 129 }, 130 expected: nil, 131 }, 132 } 133 134 for _, test := range tests { 135 handler := NewScaleHandler( 136 test.input, 137 schema.GroupVersion{Group: "apps", Version: "v1"}, 138 defaultMappings(), 139 ) 140 subresourceEntries, err := handler.ToSubresource() 141 if err != nil { 142 t.Fatalf("test %q - expected no error but got %v", test.desc, err) 143 } 144 145 if !reflect.DeepEqual(subresourceEntries, test.expected) { 146 t.Fatalf("test %q - expected output to be:\n%v\n\nbut got:\n%v", test.desc, test.expected, subresourceEntries) 147 } 148 } 149 } 150 151 func TestTransformingManagedFieldsToParent(t *testing.T) { 152 tests := []struct { 153 desc string 154 parent []metav1.ManagedFieldsEntry 155 subresource []metav1.ManagedFieldsEntry 156 expected []metav1.ManagedFieldsEntry 157 }{ 158 { 159 desc: "different-managers: apply -> update", 160 parent: []metav1.ManagedFieldsEntry{ 161 { 162 Manager: "test", 163 Operation: metav1.ManagedFieldsOperationApply, 164 APIVersion: "apps/v1", 165 FieldsType: "FieldsV1", 166 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)}, 167 }, 168 }, 169 subresource: []metav1.ManagedFieldsEntry{ 170 { 171 Manager: "scale", 172 Operation: metav1.ManagedFieldsOperationUpdate, 173 APIVersion: "autoscaling/v1", 174 FieldsType: "FieldsV1", 175 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 176 Subresource: "scale", 177 }, 178 }, 179 expected: []metav1.ManagedFieldsEntry{ 180 { 181 Manager: "test", 182 Operation: metav1.ManagedFieldsOperationApply, 183 APIVersion: "apps/v1", 184 FieldsType: "FieldsV1", 185 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)}, 186 }, 187 { 188 Manager: "scale", 189 Operation: metav1.ManagedFieldsOperationUpdate, 190 APIVersion: "apps/v1", 191 FieldsType: "FieldsV1", 192 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 193 Subresource: "scale", 194 }, 195 }, 196 }, 197 { 198 desc: "different-managers: apply -> apply", 199 parent: []metav1.ManagedFieldsEntry{ 200 { 201 Manager: "test", 202 Operation: metav1.ManagedFieldsOperationApply, 203 APIVersion: "apps/v1", 204 FieldsType: "FieldsV1", 205 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)}, 206 }, 207 }, 208 subresource: []metav1.ManagedFieldsEntry{ 209 { 210 Manager: "scale", 211 Operation: metav1.ManagedFieldsOperationApply, 212 APIVersion: "autoscaling/v1", 213 FieldsType: "FieldsV1", 214 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 215 Subresource: "scale", 216 }, 217 }, 218 expected: []metav1.ManagedFieldsEntry{ 219 { 220 Manager: "scale", 221 Operation: metav1.ManagedFieldsOperationApply, 222 APIVersion: "apps/v1", 223 FieldsType: "FieldsV1", 224 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 225 Subresource: "scale", 226 }, 227 { 228 Manager: "test", 229 Operation: metav1.ManagedFieldsOperationApply, 230 APIVersion: "apps/v1", 231 FieldsType: "FieldsV1", 232 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)}, 233 }, 234 }, 235 }, 236 { 237 desc: "different-managers: update -> update", 238 parent: []metav1.ManagedFieldsEntry{ 239 { 240 Manager: "test", 241 Operation: metav1.ManagedFieldsOperationUpdate, 242 APIVersion: "apps/v1", 243 FieldsType: "FieldsV1", 244 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)}, 245 }, 246 }, 247 subresource: []metav1.ManagedFieldsEntry{ 248 { 249 Manager: "scale", 250 Operation: metav1.ManagedFieldsOperationUpdate, 251 APIVersion: "autoscaling/v1", 252 FieldsType: "FieldsV1", 253 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 254 Subresource: "scale", 255 }, 256 }, 257 expected: []metav1.ManagedFieldsEntry{ 258 { 259 Manager: "scale", 260 Operation: metav1.ManagedFieldsOperationUpdate, 261 APIVersion: "apps/v1", 262 FieldsType: "FieldsV1", 263 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 264 Subresource: "scale", 265 }, 266 { 267 Manager: "test", 268 Operation: metav1.ManagedFieldsOperationUpdate, 269 APIVersion: "apps/v1", 270 FieldsType: "FieldsV1", 271 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)}, 272 }, 273 }, 274 }, 275 { 276 desc: "different-managers: update -> apply", 277 parent: []metav1.ManagedFieldsEntry{ 278 { 279 Manager: "test", 280 Operation: metav1.ManagedFieldsOperationUpdate, 281 APIVersion: "apps/v1", 282 FieldsType: "FieldsV1", 283 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)}, 284 }, 285 }, 286 subresource: []metav1.ManagedFieldsEntry{ 287 { 288 Manager: "scale", 289 Operation: metav1.ManagedFieldsOperationApply, 290 APIVersion: "autoscaling/v1", 291 FieldsType: "FieldsV1", 292 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 293 Subresource: "scale", 294 }, 295 }, 296 expected: []metav1.ManagedFieldsEntry{ 297 { 298 Manager: "scale", 299 Operation: metav1.ManagedFieldsOperationApply, 300 APIVersion: "apps/v1", 301 FieldsType: "FieldsV1", 302 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 303 Subresource: "scale", 304 }, 305 { 306 Manager: "test", 307 Operation: metav1.ManagedFieldsOperationUpdate, 308 APIVersion: "apps/v1", 309 FieldsType: "FieldsV1", 310 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)}, 311 }, 312 }, 313 }, 314 { 315 desc: "same manager: apply -> apply", 316 parent: []metav1.ManagedFieldsEntry{ 317 { 318 Manager: "test", 319 Operation: metav1.ManagedFieldsOperationApply, 320 APIVersion: "apps/v1", 321 FieldsType: "FieldsV1", 322 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)}, 323 }, 324 }, 325 subresource: []metav1.ManagedFieldsEntry{ 326 { 327 Manager: "test", 328 Operation: metav1.ManagedFieldsOperationApply, 329 APIVersion: "autoscaling/v1", 330 FieldsType: "FieldsV1", 331 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 332 Subresource: "scale", 333 }, 334 }, 335 expected: []metav1.ManagedFieldsEntry{ 336 { 337 Manager: "test", 338 Operation: metav1.ManagedFieldsOperationApply, 339 APIVersion: "apps/v1", 340 FieldsType: "FieldsV1", 341 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)}, 342 }, 343 { 344 Manager: "test", 345 Operation: metav1.ManagedFieldsOperationApply, 346 APIVersion: "apps/v1", 347 FieldsType: "FieldsV1", 348 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 349 Subresource: "scale", 350 }, 351 }, 352 }, 353 { 354 desc: "same manager: update -> update", 355 parent: []metav1.ManagedFieldsEntry{ 356 { 357 Manager: "test", 358 Operation: metav1.ManagedFieldsOperationUpdate, 359 APIVersion: "apps/v1", 360 FieldsType: "FieldsV1", 361 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)}, 362 }, 363 }, 364 subresource: []metav1.ManagedFieldsEntry{ 365 { 366 Manager: "test", 367 Operation: metav1.ManagedFieldsOperationUpdate, 368 APIVersion: "autoscaling/v1", 369 FieldsType: "FieldsV1", 370 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 371 Subresource: "scale", 372 }, 373 }, 374 expected: []metav1.ManagedFieldsEntry{ 375 { 376 Manager: "test", 377 Operation: metav1.ManagedFieldsOperationUpdate, 378 APIVersion: "apps/v1", 379 FieldsType: "FieldsV1", 380 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)}, 381 }, 382 { 383 Manager: "test", 384 Operation: metav1.ManagedFieldsOperationUpdate, 385 APIVersion: "apps/v1", 386 FieldsType: "FieldsV1", 387 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 388 Subresource: "scale", 389 }, 390 }, 391 }, 392 { 393 desc: "same manager: update -> apply", 394 parent: []metav1.ManagedFieldsEntry{ 395 { 396 Manager: "test", 397 Operation: metav1.ManagedFieldsOperationUpdate, 398 APIVersion: "apps/v1", 399 FieldsType: "FieldsV1", 400 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)}, 401 }, 402 }, 403 subresource: []metav1.ManagedFieldsEntry{ 404 { 405 Manager: "test", 406 Operation: metav1.ManagedFieldsOperationApply, 407 APIVersion: "autoscaling/v1", 408 FieldsType: "FieldsV1", 409 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 410 Subresource: "scale", 411 }, 412 }, 413 expected: []metav1.ManagedFieldsEntry{ 414 { 415 Manager: "test", 416 Operation: metav1.ManagedFieldsOperationApply, 417 APIVersion: "apps/v1", 418 FieldsType: "FieldsV1", 419 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 420 Subresource: "scale", 421 }, 422 { 423 Manager: "test", 424 Operation: metav1.ManagedFieldsOperationUpdate, 425 APIVersion: "apps/v1", 426 FieldsType: "FieldsV1", 427 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)}, 428 }, 429 }, 430 }, 431 { 432 desc: "same manager: apply -> update", 433 parent: []metav1.ManagedFieldsEntry{ 434 { 435 Manager: "test", 436 Operation: metav1.ManagedFieldsOperationApply, 437 APIVersion: "apps/v1", 438 FieldsType: "FieldsV1", 439 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)}, 440 }, 441 }, 442 subresource: []metav1.ManagedFieldsEntry{ 443 { 444 Manager: "test", 445 Operation: metav1.ManagedFieldsOperationUpdate, 446 APIVersion: "autoscaling/v1", 447 FieldsType: "FieldsV1", 448 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 449 Subresource: "scale", 450 }, 451 }, 452 expected: []metav1.ManagedFieldsEntry{ 453 { 454 Manager: "test", 455 Operation: metav1.ManagedFieldsOperationApply, 456 APIVersion: "apps/v1", 457 FieldsType: "FieldsV1", 458 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)}, 459 }, 460 { 461 Manager: "test", 462 Operation: metav1.ManagedFieldsOperationUpdate, 463 APIVersion: "apps/v1", 464 FieldsType: "FieldsV1", 465 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 466 Subresource: "scale", 467 }, 468 }, 469 }, 470 { 471 desc: "subresource doesn't own the path anymore", 472 parent: []metav1.ManagedFieldsEntry{ 473 { 474 Manager: "test", 475 Operation: metav1.ManagedFieldsOperationApply, 476 APIVersion: "apps/v1", 477 FieldsType: "FieldsV1", 478 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)}, 479 }, 480 }, 481 subresource: []metav1.ManagedFieldsEntry{ 482 { 483 Manager: "scale", 484 Operation: metav1.ManagedFieldsOperationUpdate, 485 APIVersion: "autoscaling/v1", 486 FieldsType: "FieldsV1", 487 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:status":{"f:replicas":{}}}`)}, 488 Subresource: "scale", 489 }, 490 }, 491 expected: []metav1.ManagedFieldsEntry{ 492 { 493 Manager: "test", 494 Operation: metav1.ManagedFieldsOperationApply, 495 APIVersion: "apps/v1", 496 FieldsType: "FieldsV1", 497 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)}, 498 }, 499 }, 500 }, 501 { 502 desc: "Subresource steals all the fields of the parent resource", 503 parent: []metav1.ManagedFieldsEntry{ 504 { 505 Manager: "test", 506 Operation: metav1.ManagedFieldsOperationApply, 507 APIVersion: "apps/v1", 508 FieldsType: "FieldsV1", 509 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 510 }, 511 }, 512 subresource: []metav1.ManagedFieldsEntry{ 513 { 514 Manager: "scale", 515 Operation: metav1.ManagedFieldsOperationUpdate, 516 APIVersion: "autoscaling/v1", 517 FieldsType: "FieldsV1", 518 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 519 Subresource: "scale", 520 }, 521 }, 522 expected: []metav1.ManagedFieldsEntry{ 523 { 524 Manager: "scale", 525 Operation: metav1.ManagedFieldsOperationUpdate, 526 APIVersion: "apps/v1", 527 FieldsType: "FieldsV1", 528 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 529 Subresource: "scale", 530 }, 531 }, 532 }, 533 { 534 desc: "apply without stealing", 535 parent: []metav1.ManagedFieldsEntry{ 536 { 537 Manager: "test", 538 Operation: metav1.ManagedFieldsOperationApply, 539 APIVersion: "apps/v1", 540 FieldsType: "FieldsV1", 541 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)}, 542 }, 543 }, 544 subresource: []metav1.ManagedFieldsEntry{ 545 { 546 Manager: "test", 547 Operation: metav1.ManagedFieldsOperationApply, 548 APIVersion: "autoscaling/v1", 549 FieldsType: "FieldsV1", 550 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 551 }, 552 { 553 Manager: "test", 554 Operation: metav1.ManagedFieldsOperationApply, 555 APIVersion: "autoscaling/v1", 556 FieldsType: "FieldsV1", 557 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 558 Subresource: "scale", 559 }, 560 }, 561 expected: []metav1.ManagedFieldsEntry{ 562 { 563 Manager: "test", 564 Operation: metav1.ManagedFieldsOperationApply, 565 APIVersion: "apps/v1", 566 FieldsType: "FieldsV1", 567 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)}, 568 }, 569 { 570 Manager: "test", 571 Operation: metav1.ManagedFieldsOperationApply, 572 APIVersion: "apps/v1", 573 FieldsType: "FieldsV1", 574 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 575 Subresource: "scale", 576 }, 577 }, 578 }, 579 { 580 desc: "drops the entry if the api version is unknown", 581 parent: []metav1.ManagedFieldsEntry{ 582 { 583 Manager: "test", 584 Operation: metav1.ManagedFieldsOperationApply, 585 APIVersion: "apps/v1", 586 FieldsType: "FieldsV1", 587 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 588 }, 589 { 590 Manager: "another-manager", 591 Operation: metav1.ManagedFieldsOperationApply, 592 APIVersion: "apps/v10", 593 FieldsType: "FieldsV1", 594 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)}, 595 }, 596 }, 597 subresource: []metav1.ManagedFieldsEntry{ 598 { 599 Manager: "scale", 600 Operation: metav1.ManagedFieldsOperationUpdate, 601 APIVersion: "autoscaling/v1", 602 FieldsType: "FieldsV1", 603 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 604 }, 605 }, 606 expected: []metav1.ManagedFieldsEntry{ 607 { 608 Manager: "scale", 609 Operation: metav1.ManagedFieldsOperationUpdate, 610 APIVersion: "apps/v1", 611 FieldsType: "FieldsV1", 612 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 613 }, 614 }, 615 }, 616 } 617 618 for _, test := range tests { 619 t.Run(test.desc, func(t *testing.T) { 620 handler := NewScaleHandler( 621 test.parent, 622 schema.GroupVersion{Group: "apps", Version: "v1"}, 623 defaultMappings(), 624 ) 625 parentEntries, err := handler.ToParent(test.subresource) 626 if err != nil { 627 t.Fatalf("test: %q - expected no error but got %v", test.desc, err) 628 } 629 if !reflect.DeepEqual(parentEntries, test.expected) { 630 t.Fatalf("test: %q - expected output to be:\n%v\n\nbut got:\n%v", test.desc, test.expected, parentEntries) 631 } 632 }) 633 } 634 } 635 636 func TestTransformingManagedFieldsToParentMultiVersion(t *testing.T) { 637 tests := []struct { 638 desc string 639 groupVersion schema.GroupVersion 640 mappings ResourcePathMappings 641 parent []metav1.ManagedFieldsEntry 642 subresource []metav1.ManagedFieldsEntry 643 expected []metav1.ManagedFieldsEntry 644 }{ 645 { 646 desc: "multi-version", 647 groupVersion: schema.GroupVersion{Group: "apps", Version: "v1"}, 648 mappings: ResourcePathMappings{ 649 "apps/v1": fieldpath.MakePathOrDie("spec", "the-replicas"), 650 "apps/v2": fieldpath.MakePathOrDie("spec", "not-the-replicas"), 651 }, 652 parent: []metav1.ManagedFieldsEntry{ 653 { 654 Manager: "test", 655 Operation: metav1.ManagedFieldsOperationApply, 656 APIVersion: "apps/v1", 657 FieldsType: "FieldsV1", 658 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:the-replicas":{},"f:selector":{}}}`)}, 659 }, 660 { 661 Manager: "test-other", 662 Operation: metav1.ManagedFieldsOperationApply, 663 APIVersion: "apps/v2", 664 FieldsType: "FieldsV1", 665 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:not-the-replicas":{},"f:selector":{}}}`)}, 666 }, 667 }, 668 subresource: []metav1.ManagedFieldsEntry{ 669 { 670 Manager: "scale", 671 Operation: metav1.ManagedFieldsOperationUpdate, 672 APIVersion: "autoscaling/v1", 673 FieldsType: "FieldsV1", 674 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 675 Subresource: "scale", 676 }, 677 }, 678 expected: []metav1.ManagedFieldsEntry{ 679 { 680 Manager: "test", 681 Operation: metav1.ManagedFieldsOperationApply, 682 APIVersion: "apps/v1", 683 FieldsType: "FieldsV1", 684 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)}, 685 }, 686 { 687 Manager: "test-other", 688 Operation: metav1.ManagedFieldsOperationApply, 689 APIVersion: "apps/v2", 690 FieldsType: "FieldsV1", 691 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)}, 692 }, 693 { 694 Manager: "scale", 695 Operation: metav1.ManagedFieldsOperationUpdate, 696 APIVersion: "apps/v1", 697 FieldsType: "FieldsV1", 698 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:the-replicas":{}}}`)}, 699 Subresource: "scale", 700 }, 701 }, 702 }, 703 { 704 desc: "Custom resource without scale subresource, scaling a version with `scale`", 705 groupVersion: schema.GroupVersion{Group: "mygroup", Version: "v1"}, 706 mappings: ResourcePathMappings{ 707 "mygroup/v1": fieldpath.MakePathOrDie("spec", "the-replicas"), 708 "mygroup/v2": nil, 709 }, 710 parent: []metav1.ManagedFieldsEntry{ 711 { 712 Manager: "test", 713 Operation: metav1.ManagedFieldsOperationApply, 714 APIVersion: "mygroup/v1", 715 FieldsType: "FieldsV1", 716 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:the-replicas":{},"f:selector":{}}}`)}, 717 }, 718 { 719 Manager: "test-other", 720 Operation: metav1.ManagedFieldsOperationApply, 721 APIVersion: "mygroup/v2", 722 FieldsType: "FieldsV1", 723 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:test-other":{}}}`)}, 724 }, 725 }, 726 subresource: []metav1.ManagedFieldsEntry{ 727 { 728 Manager: "scale", 729 Operation: metav1.ManagedFieldsOperationUpdate, 730 APIVersion: "autoscaling/v1", 731 FieldsType: "FieldsV1", 732 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)}, 733 Subresource: "scale", 734 }, 735 }, 736 expected: []metav1.ManagedFieldsEntry{ 737 { 738 Manager: "test", 739 Operation: metav1.ManagedFieldsOperationApply, 740 APIVersion: "mygroup/v1", 741 FieldsType: "FieldsV1", 742 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)}, 743 }, 744 { 745 Manager: "test-other", 746 Operation: metav1.ManagedFieldsOperationApply, 747 APIVersion: "mygroup/v2", 748 FieldsType: "FieldsV1", 749 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:test-other":{}}}`)}, 750 }, 751 { 752 Manager: "scale", 753 Operation: metav1.ManagedFieldsOperationUpdate, 754 APIVersion: "mygroup/v1", 755 FieldsType: "FieldsV1", 756 FieldsV1: &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:the-replicas":{}}}`)}, 757 Subresource: "scale", 758 }, 759 }, 760 }, 761 } 762 763 for _, test := range tests { 764 t.Run(test.desc, func(t *testing.T) { 765 handler := NewScaleHandler( 766 test.parent, 767 test.groupVersion, 768 test.mappings, 769 ) 770 parentEntries, err := handler.ToParent(test.subresource) 771 if err != nil { 772 t.Fatalf("test: %q - expected no error but got %v", test.desc, err) 773 } 774 if !reflect.DeepEqual(parentEntries, test.expected) { 775 t.Fatalf("test: %q - expected output to be:\n%v\n\nbut got:\n%v", test.desc, test.expected, parentEntries) 776 } 777 }) 778 } 779 } 780 781 func defaultMappings() ResourcePathMappings { 782 return ResourcePathMappings{ 783 "apps/v1": fieldpath.MakePathOrDie("spec", "replicas"), 784 } 785 }