k8s.io/apimachinery@v0.29.2/pkg/util/managedfields/fieldmanager_test.go (about) 1 /* 2 Copyright 2019 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_test 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "net/http" 23 "os" 24 "path/filepath" 25 "reflect" 26 "strings" 27 "testing" 28 "time" 29 30 apierrors "k8s.io/apimachinery/pkg/api/errors" 31 "k8s.io/apimachinery/pkg/api/meta" 32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 33 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 34 "k8s.io/apimachinery/pkg/runtime" 35 "k8s.io/apimachinery/pkg/runtime/schema" 36 "k8s.io/apimachinery/pkg/util/managedfields" 37 "k8s.io/apimachinery/pkg/util/managedfields/internal" 38 "k8s.io/apimachinery/pkg/util/managedfields/managedfieldstest" 39 yamlutil "k8s.io/apimachinery/pkg/util/yaml" 40 "k8s.io/kube-openapi/pkg/validation/spec" 41 "sigs.k8s.io/yaml" 42 ) 43 44 var fakeTypeConverter = func() managedfields.TypeConverter { 45 data, err := os.ReadFile(filepath.Join(strings.Repeat(".."+string(filepath.Separator), 7), 46 "api", "openapi-spec", "swagger.json")) 47 if err != nil { 48 panic(err) 49 } 50 swag := spec.Swagger{} 51 if err := json.Unmarshal(data, &swag); err != nil { 52 panic(err) 53 } 54 convertedDefs := map[string]*spec.Schema{} 55 for k, v := range swag.Definitions { 56 vCopy := v 57 convertedDefs[k] = &vCopy 58 } 59 typeConverter, err := managedfields.NewTypeConverter(convertedDefs, false) 60 if err != nil { 61 panic(err) 62 } 63 return typeConverter 64 }() 65 66 // TestUpdateApplyConflict tests that applying to an object, which 67 // wasn't created by apply, will give conflicts 68 func TestUpdateApplyConflict(t *testing.T) { 69 f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) 70 71 patch := []byte(`{ 72 "apiVersion": "apps/v1", 73 "kind": "Deployment", 74 "metadata": { 75 "name": "deployment", 76 "labels": {"app": "nginx"} 77 }, 78 "spec": { 79 "replicas": 3, 80 "selector": { 81 "matchLabels": { 82 "app": "nginx" 83 } 84 }, 85 "template": { 86 "metadata": { 87 "labels": { 88 "app": "nginx" 89 } 90 }, 91 "spec": { 92 "containers": [{ 93 "name": "nginx", 94 "image": "nginx:latest" 95 }] 96 } 97 } 98 } 99 }`) 100 newObj := &unstructured.Unstructured{Object: map[string]interface{}{}} 101 if err := yaml.Unmarshal(patch, &newObj.Object); err != nil { 102 t.Fatalf("error decoding YAML: %v", err) 103 } 104 105 if err := f.Update(newObj, "fieldmanager_test"); err != nil { 106 t.Fatalf("failed to apply object: %v", err) 107 } 108 109 appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} 110 if err := yaml.Unmarshal([]byte(`{ 111 "apiVersion": "apps/v1", 112 "kind": "Deployment", 113 "metadata": { 114 "name": "deployment", 115 }, 116 "spec": { 117 "replicas": 101, 118 } 119 }`), &appliedObj.Object); err != nil { 120 t.Fatalf("error decoding YAML: %v", err) 121 } 122 123 err := f.Apply(appliedObj, "fieldmanager_conflict", false) 124 if err == nil || !apierrors.IsConflict(err) { 125 t.Fatalf("Expecting to get conflicts but got %v", err) 126 } 127 } 128 129 func TestApplyStripsFields(t *testing.T) { 130 f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) 131 132 newObj := &unstructured.Unstructured{ 133 Object: map[string]interface{}{ 134 "apiVersion": "apps/v1", 135 "kind": "Deployment", 136 }, 137 } 138 139 newObj.SetName("b") 140 newObj.SetNamespace("b") 141 newObj.SetUID("b") 142 newObj.SetGeneration(0) 143 newObj.SetResourceVersion("b") 144 newObj.SetCreationTimestamp(metav1.NewTime(time.Now())) 145 newObj.SetManagedFields([]metav1.ManagedFieldsEntry{ 146 { 147 Manager: "update", 148 Operation: metav1.ManagedFieldsOperationApply, 149 APIVersion: "apps/v1", 150 }, 151 }) 152 if err := f.Update(newObj, "fieldmanager_test"); err != nil { 153 t.Fatalf("failed to apply object: %v", err) 154 } 155 156 if m := f.ManagedFields(); len(m) != 0 { 157 t.Fatalf("fields did not get stripped: %v", m) 158 } 159 } 160 161 func TestVersionCheck(t *testing.T) { 162 f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) 163 164 appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} 165 if err := yaml.Unmarshal([]byte(`{ 166 "apiVersion": "apps/v1", 167 "kind": "Deployment", 168 }`), &appliedObj.Object); err != nil { 169 t.Fatalf("error decoding YAML: %v", err) 170 } 171 172 // patch has 'apiVersion: apps/v1' and live version is apps/v1 -> no errors 173 err := f.Apply(appliedObj, "fieldmanager_test", false) 174 if err != nil { 175 t.Fatalf("failed to apply object: %v", err) 176 } 177 178 appliedObj = &unstructured.Unstructured{Object: map[string]interface{}{}} 179 if err := yaml.Unmarshal([]byte(`{ 180 "apiVersion": "apps/v1beta1", 181 "kind": "Deployment", 182 }`), &appliedObj.Object); err != nil { 183 t.Fatalf("error decoding YAML: %v", err) 184 } 185 186 // patch has 'apiVersion: apps/v1beta1' but live version is apps/v1 -> error 187 err = f.Apply(appliedObj, "fieldmanager_test", false) 188 if err == nil { 189 t.Fatalf("expected an error from mismatched patch and live versions") 190 } 191 switch typ := err.(type) { 192 default: 193 t.Fatalf("expected error to be of type %T was %T (%v)", apierrors.StatusError{}, typ, err) 194 case apierrors.APIStatus: 195 if typ.Status().Code != http.StatusBadRequest { 196 t.Fatalf("expected status code to be %d but was %d", 197 http.StatusBadRequest, typ.Status().Code) 198 } 199 } 200 } 201 202 func TestVersionCheckDoesNotPanic(t *testing.T) { 203 f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) 204 205 appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} 206 if err := yaml.Unmarshal([]byte(`{ 207 "apiVersion": "apps/v1", 208 "kind": "Deployment", 209 }`), &appliedObj.Object); err != nil { 210 t.Fatalf("error decoding YAML: %v", err) 211 } 212 213 // patch has 'apiVersion: apps/v1' and live version is apps/v1 -> no errors 214 err := f.Apply(appliedObj, "fieldmanager_test", false) 215 if err != nil { 216 t.Fatalf("failed to apply object: %v", err) 217 } 218 219 appliedObj = &unstructured.Unstructured{Object: map[string]interface{}{}} 220 if err := yaml.Unmarshal([]byte(`{ 221 }`), &appliedObj.Object); err != nil { 222 t.Fatalf("error decoding YAML: %v", err) 223 } 224 225 // patch has 'apiVersion: apps/v2' but live version is apps/v1 -> error 226 err = f.Apply(appliedObj, "fieldmanager_test", false) 227 if err == nil { 228 t.Fatalf("expected an error from mismatched patch and live versions") 229 } 230 switch typ := err.(type) { 231 default: 232 t.Fatalf("expected error to be of type %T was %T (%v)", apierrors.StatusError{}, typ, err) 233 case apierrors.APIStatus: 234 if typ.Status().Code != http.StatusBadRequest { 235 t.Fatalf("expected status code to be %d but was %d", 236 http.StatusBadRequest, typ.Status().Code) 237 } 238 } 239 } 240 241 func TestApplyDoesNotStripLabels(t *testing.T) { 242 f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) 243 244 appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} 245 if err := yaml.Unmarshal([]byte(`{ 246 "apiVersion": "v1", 247 "kind": "Pod", 248 "metadata": { 249 "labels": { 250 "a": "b" 251 }, 252 } 253 }`), &appliedObj.Object); err != nil { 254 t.Fatalf("error decoding YAML: %v", err) 255 } 256 257 err := f.Apply(appliedObj, "fieldmanager_test", false) 258 if err != nil { 259 t.Fatalf("failed to apply object: %v", err) 260 } 261 262 if m := f.ManagedFields(); len(m) != 1 { 263 t.Fatalf("labels shouldn't get stripped on apply: %v", m) 264 } 265 } 266 267 func getObjectBytes(file string) []byte { 268 s, err := os.ReadFile(file) 269 if err != nil { 270 panic(err) 271 } 272 return s 273 } 274 275 func TestApplyNewObject(t *testing.T) { 276 tests := []struct { 277 gvk schema.GroupVersionKind 278 obj []byte 279 }{ 280 { 281 gvk: schema.FromAPIVersionAndKind("v1", "Pod"), 282 obj: getObjectBytes("pod.yaml"), 283 }, 284 { 285 gvk: schema.FromAPIVersionAndKind("v1", "Node"), 286 obj: getObjectBytes("node.yaml"), 287 }, 288 { 289 gvk: schema.FromAPIVersionAndKind("v1", "Endpoints"), 290 obj: getObjectBytes("endpoints.yaml"), 291 }, 292 } 293 294 for _, test := range tests { 295 t.Run(test.gvk.String(), func(t *testing.T) { 296 f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, test.gvk) 297 298 appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} 299 if err := yaml.Unmarshal(test.obj, &appliedObj.Object); err != nil { 300 t.Fatalf("error decoding YAML: %v", err) 301 } 302 303 if err := f.Apply(appliedObj, "fieldmanager_test", false); err != nil { 304 t.Fatal(err) 305 } 306 }) 307 } 308 } 309 310 func TestApplyFailsWithManagedFields(t *testing.T) { 311 f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) 312 313 appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} 314 if err := yaml.Unmarshal([]byte(`{ 315 "apiVersion": "v1", 316 "kind": "Pod", 317 "metadata": { 318 "managedFields": [ 319 { 320 "manager": "test", 321 } 322 ] 323 } 324 }`), &appliedObj.Object); err != nil { 325 t.Fatalf("error decoding YAML: %v", err) 326 } 327 328 err := f.Apply(appliedObj, "fieldmanager_test", false) 329 330 if err == nil { 331 t.Fatalf("successfully applied with set managed fields") 332 } 333 } 334 335 func TestApplySuccessWithNoManagedFields(t *testing.T) { 336 f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) 337 338 appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} 339 if err := yaml.Unmarshal([]byte(`{ 340 "apiVersion": "v1", 341 "kind": "Pod", 342 "metadata": { 343 "labels": { 344 "a": "b" 345 }, 346 } 347 }`), &appliedObj.Object); err != nil { 348 t.Fatalf("error decoding YAML: %v", err) 349 } 350 err := f.Apply(appliedObj, "fieldmanager_test", false) 351 352 if err != nil { 353 t.Fatalf("failed to apply object: %v", err) 354 } 355 } 356 357 // Run an update and apply, and make sure that nothing has changed. 358 func TestNoOpChanges(t *testing.T) { 359 f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) 360 361 obj := &unstructured.Unstructured{Object: map[string]interface{}{}} 362 if err := yaml.Unmarshal([]byte(`{ 363 "apiVersion": "v1", 364 "kind": "Pod", 365 "metadata": { 366 "labels": { 367 "a": "b" 368 }, 369 "creationTimestamp": null, 370 } 371 }`), &obj.Object); err != nil { 372 t.Fatalf("error decoding YAML: %v", err) 373 } 374 if err := f.Apply(obj.DeepCopyObject(), "fieldmanager_test_apply", false); err != nil { 375 t.Fatalf("failed to apply object: %v", err) 376 } 377 before := f.Live() 378 // Wait to make sure the timestamp is different 379 time.Sleep(time.Second) 380 // Applying with a different fieldmanager will create an entry.. 381 if err := f.Apply(obj.DeepCopyObject(), "fieldmanager_test_apply_other", false); err != nil { 382 t.Fatalf("failed to update object: %v", err) 383 } 384 if reflect.DeepEqual(before, f.Live()) { 385 t.Fatalf("Applying no-op apply with new manager didn't change object: \n%v", f.Live()) 386 } 387 before = f.Live() 388 // Wait to make sure the timestamp is different 389 time.Sleep(time.Second) 390 if err := f.Update(obj.DeepCopyObject(), "fieldmanager_test_update"); err != nil { 391 t.Fatalf("failed to update object: %v", err) 392 } 393 if !reflect.DeepEqual(before, f.Live()) { 394 t.Fatalf("No-op update has changed the object:\n%v\n---\n%v", before, f.Live()) 395 } 396 before = f.Live() 397 // Wait to make sure the timestamp is different 398 time.Sleep(time.Second) 399 if err := f.Apply(obj.DeepCopyObject(), "fieldmanager_test_apply", true); err != nil { 400 t.Fatalf("failed to re-apply object: %v", err) 401 } 402 if !reflect.DeepEqual(before, f.Live()) { 403 t.Fatalf("No-op apply has changed the object:\n%v\n---\n%v", before, f.Live()) 404 } 405 } 406 407 // Tests that one can reset the managedFields by sending either an empty 408 // list 409 func TestResetManagedFieldsEmptyList(t *testing.T) { 410 f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) 411 412 obj := &unstructured.Unstructured{Object: map[string]interface{}{}} 413 if err := yaml.Unmarshal([]byte(`{ 414 "apiVersion": "v1", 415 "kind": "Pod", 416 "metadata": { 417 "labels": { 418 "a": "b" 419 }, 420 } 421 }`), &obj.Object); err != nil { 422 t.Fatalf("error decoding YAML: %v", err) 423 } 424 if err := f.Apply(obj, "fieldmanager_test_apply", false); err != nil { 425 t.Fatalf("failed to apply object: %v", err) 426 } 427 428 if err := yaml.Unmarshal([]byte(`{ 429 "apiVersion": "v1", 430 "kind": "Pod", 431 "metadata": { 432 "managedFields": [], 433 "labels": { 434 "a": "b" 435 }, 436 } 437 }`), &obj.Object); err != nil { 438 t.Fatalf("error decoding YAML: %v", err) 439 } 440 if err := f.Update(obj, "update_manager"); err != nil { 441 t.Fatalf("failed to update with empty manager: %v", err) 442 } 443 444 if len(f.ManagedFields()) != 0 { 445 t.Fatalf("failed to reset managedFields: %v", f.ManagedFields()) 446 } 447 } 448 449 // Tests that one can reset the managedFields by sending either a list with one empty item. 450 func TestResetManagedFieldsEmptyItem(t *testing.T) { 451 f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod")) 452 453 obj := &unstructured.Unstructured{Object: map[string]interface{}{}} 454 if err := yaml.Unmarshal([]byte(`{ 455 "apiVersion": "v1", 456 "kind": "Pod", 457 "metadata": { 458 "labels": { 459 "a": "b" 460 }, 461 } 462 }`), &obj.Object); err != nil { 463 t.Fatalf("error decoding YAML: %v", err) 464 } 465 if err := f.Apply(obj, "fieldmanager_test_apply", false); err != nil { 466 t.Fatalf("failed to apply object: %v", err) 467 } 468 469 if err := yaml.Unmarshal([]byte(`{ 470 "apiVersion": "v1", 471 "kind": "Pod", 472 "metadata": { 473 "managedFields": [{}], 474 "labels": { 475 "a": "b" 476 }, 477 } 478 }`), &obj.Object); err != nil { 479 t.Fatalf("error decoding YAML: %v", err) 480 } 481 if err := f.Update(obj, "update_manager"); err != nil { 482 t.Fatalf("failed to update with empty manager: %v", err) 483 } 484 485 if len(f.ManagedFields()) != 0 { 486 t.Fatalf("failed to reset managedFields: %v", f.ManagedFields()) 487 } 488 } 489 490 func TestServerSideApplyWithInvalidLastApplied(t *testing.T) { 491 f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) 492 493 // create object with client-side apply 494 newObj := &unstructured.Unstructured{Object: map[string]interface{}{}} 495 deployment := []byte(` 496 apiVersion: apps/v1 497 kind: Deployment 498 metadata: 499 name: my-deployment 500 labels: 501 app: my-app-v1 502 spec: 503 replicas: 1 504 `) 505 if err := yaml.Unmarshal(deployment, &newObj.Object); err != nil { 506 t.Errorf("error decoding YAML: %v", err) 507 } 508 509 invalidLastApplied := "invalid-object" 510 if err := internal.SetLastApplied(newObj, invalidLastApplied); err != nil { 511 t.Errorf("failed to set last applied: %v", err) 512 } 513 514 if err := f.Update(newObj, "kubectl-client-side-apply-test"); err != nil { 515 t.Errorf("failed to update object: %v", err) 516 } 517 518 lastApplied, err := getLastApplied(f.Live()) 519 if err != nil { 520 t.Errorf("failed to get last applied: %v", err) 521 } 522 if lastApplied != invalidLastApplied { 523 t.Errorf("expected last applied annotation to be set to %q, but got: %q", invalidLastApplied, lastApplied) 524 } 525 526 // upgrade management of the object from client-side apply to server-side apply 527 appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} 528 appliedDeployment := []byte(` 529 apiVersion: apps/v1 530 kind: Deployment 531 metadata: 532 name: my-deployment 533 labels: 534 app: my-app-v2 535 spec: 536 replicas: 100 537 `) 538 if err := yaml.Unmarshal(appliedDeployment, &appliedObj.Object); err != nil { 539 t.Errorf("error decoding YAML: %v", err) 540 } 541 542 if err := f.Apply(appliedObj, "kubectl", false); err == nil || !apierrors.IsConflict(err) { 543 t.Errorf("expected conflict when applying with invalid last-applied annotation, but got no error for object: \n%+v", appliedObj) 544 } 545 546 lastApplied, err = getLastApplied(f.Live()) 547 if err != nil { 548 t.Errorf("failed to get last applied: %v", err) 549 } 550 if lastApplied != invalidLastApplied { 551 t.Errorf("expected last applied annotation to be NOT be updated, but got: %q", lastApplied) 552 } 553 554 // force server-side apply should work and fix the annotation 555 if err := f.Apply(appliedObj, "kubectl", true); err != nil { 556 t.Errorf("failed to force server-side apply with: %v", err) 557 } 558 559 lastApplied, err = getLastApplied(f.Live()) 560 if err != nil { 561 t.Errorf("failed to get last applied: %v", err) 562 } 563 if lastApplied == invalidLastApplied || 564 !strings.Contains(lastApplied, "my-app-v2") { 565 t.Errorf("expected last applied annotation to be updated, but got: %q", lastApplied) 566 } 567 } 568 569 func TestInteropForClientSideApplyAndServerSideApply(t *testing.T) { 570 f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) 571 572 // create object with client-side apply 573 newObj := &unstructured.Unstructured{Object: map[string]interface{}{}} 574 deployment := []byte(` 575 apiVersion: apps/v1 576 kind: Deployment 577 metadata: 578 name: my-deployment 579 labels: 580 app: my-app 581 spec: 582 replicas: 100 583 selector: 584 matchLabels: 585 app: my-app 586 template: 587 metadata: 588 labels: 589 app: my-app 590 spec: 591 containers: 592 - name: my-c 593 image: my-image-v1 594 `) 595 if err := yaml.Unmarshal(deployment, &newObj.Object); err != nil { 596 t.Errorf("error decoding YAML: %v", err) 597 } 598 if err := setLastAppliedFromEncoded(newObj, deployment); err != nil { 599 t.Errorf("failed to set last applied: %v", err) 600 } 601 602 if err := f.Update(newObj, "kubectl-client-side-apply-test"); err != nil { 603 t.Errorf("failed to update object: %v", err) 604 } 605 lastApplied, err := getLastApplied(f.Live()) 606 if err != nil { 607 t.Errorf("failed to get last applied: %v", err) 608 } 609 if !strings.Contains(lastApplied, "my-image-v1") { 610 t.Errorf("expected last applied annotation to be set properly, but got: %q", lastApplied) 611 } 612 613 // upgrade management of the object from client-side apply to server-side apply 614 appliedObj := &unstructured.Unstructured{Object: map[string]interface{}{}} 615 appliedDeployment := []byte(` 616 apiVersion: apps/v1 617 kind: Deployment 618 metadata: 619 name: my-deployment 620 labels: 621 app: my-app-v2 # change 622 spec: 623 replicas: 8 # change 624 selector: 625 matchLabels: 626 app: my-app-v2 # change 627 template: 628 metadata: 629 labels: 630 app: my-app-v2 # change 631 spec: 632 containers: 633 - name: my-c 634 image: my-image-v2 # change 635 `) 636 if err := yaml.Unmarshal(appliedDeployment, &appliedObj.Object); err != nil { 637 t.Errorf("error decoding YAML: %v", err) 638 } 639 640 if err := f.Apply(appliedObj, "kubectl", false); err != nil { 641 t.Errorf("error applying object: %v", err) 642 } 643 644 lastApplied, err = getLastApplied(f.Live()) 645 if err != nil { 646 t.Errorf("failed to get last applied: %v", err) 647 } 648 if !strings.Contains(lastApplied, "my-image-v2") { 649 t.Errorf("expected last applied annotation to be updated, but got: %q", lastApplied) 650 } 651 } 652 653 func TestNoTrackManagedFieldsForClientSideApply(t *testing.T) { 654 f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, schema.FromAPIVersionAndKind("apps/v1", "Deployment")) 655 656 // create object 657 newObj := &unstructured.Unstructured{Object: map[string]interface{}{}} 658 deployment := []byte(` 659 apiVersion: apps/v1 660 kind: Deployment 661 metadata: 662 name: my-deployment 663 labels: 664 app: my-app 665 spec: 666 replicas: 100 667 `) 668 if err := yaml.Unmarshal(deployment, &newObj.Object); err != nil { 669 t.Errorf("error decoding YAML: %v", err) 670 } 671 if err := f.Update(newObj, "test_kubectl_create"); err != nil { 672 t.Errorf("failed to update object: %v", err) 673 } 674 if m := f.ManagedFields(); len(m) == 0 { 675 t.Errorf("expected to have managed fields, but got: %v", m) 676 } 677 678 // stop tracking managed fields 679 newObj = &unstructured.Unstructured{Object: map[string]interface{}{}} 680 deployment = []byte(` 681 apiVersion: apps/v1 682 kind: Deployment 683 metadata: 684 name: my-deployment 685 managedFields: [] # stop tracking managed fields 686 labels: 687 app: my-app 688 spec: 689 replicas: 100 690 `) 691 if err := yaml.Unmarshal(deployment, &newObj.Object); err != nil { 692 t.Errorf("error decoding YAML: %v", err) 693 } 694 newObj.SetUID("nonempty") 695 if err := f.Update(newObj, "test_kubectl_replace"); err != nil { 696 t.Errorf("failed to update object: %v", err) 697 } 698 if m := f.ManagedFields(); len(m) != 0 { 699 t.Errorf("expected to have stop tracking managed fields, but got: %v", m) 700 } 701 702 // check that we still don't track managed fields 703 newObj = &unstructured.Unstructured{Object: map[string]interface{}{}} 704 deployment = []byte(` 705 apiVersion: apps/v1 706 kind: Deployment 707 metadata: 708 name: my-deployment 709 labels: 710 app: my-app 711 spec: 712 replicas: 100 713 `) 714 if err := yaml.Unmarshal(deployment, &newObj.Object); err != nil { 715 t.Errorf("error decoding YAML: %v", err) 716 } 717 if err := setLastAppliedFromEncoded(newObj, deployment); err != nil { 718 t.Errorf("failed to set last applied: %v", err) 719 } 720 if err := f.Update(newObj, "test_k_client_side_apply"); err != nil { 721 t.Errorf("failed to update object: %v", err) 722 } 723 if m := f.ManagedFields(); len(m) != 0 { 724 t.Errorf("expected to continue to not track managed fields, but got: %v", m) 725 } 726 lastApplied, err := getLastApplied(f.Live()) 727 if err != nil { 728 t.Errorf("failed to get last applied: %v", err) 729 } 730 if !strings.Contains(lastApplied, "my-app") { 731 t.Errorf("expected last applied annotation to be set properly, but got: %q", lastApplied) 732 } 733 734 // start tracking managed fields 735 newObj = &unstructured.Unstructured{Object: map[string]interface{}{}} 736 deployment = []byte(` 737 apiVersion: apps/v1 738 kind: Deployment 739 metadata: 740 name: my-deployment 741 labels: 742 app: my-app 743 spec: 744 replicas: 100 745 `) 746 if err := yaml.Unmarshal(deployment, &newObj.Object); err != nil { 747 t.Errorf("error decoding YAML: %v", err) 748 } 749 if err := f.Apply(newObj, "test_server_side_apply_without_upgrade", false); err != nil { 750 t.Errorf("error applying object: %v", err) 751 } 752 if m := f.ManagedFields(); len(m) < 2 { 753 t.Errorf("expected to start tracking managed fields with at least 2 field managers, but got: %v", m) 754 } 755 if e, a := "test_server_side_apply_without_upgrade", f.ManagedFields()[0].Manager; e != a { 756 t.Fatalf("exected first manager name to be %v, but got %v: %#v", e, a, f.ManagedFields()) 757 } 758 if e, a := "before-first-apply", f.ManagedFields()[1].Manager; e != a { 759 t.Fatalf("exected second manager name to be %v, but got %v: %#v", e, a, f.ManagedFields()) 760 } 761 762 // upgrade management of the object from client-side apply to server-side apply 763 newObj = &unstructured.Unstructured{Object: map[string]interface{}{}} 764 deployment = []byte(` 765 apiVersion: apps/v1 766 kind: Deployment 767 metadata: 768 name: my-deployment 769 labels: 770 app: my-app-v2 # change 771 spec: 772 replicas: 8 # change 773 `) 774 if err := yaml.Unmarshal(deployment, &newObj.Object); err != nil { 775 t.Errorf("error decoding YAML: %v", err) 776 } 777 if err := f.Apply(newObj, "kubectl", false); err != nil { 778 t.Errorf("error applying object: %v", err) 779 } 780 if m := f.ManagedFields(); len(m) == 0 { 781 t.Errorf("expected to track managed fields, but got: %v", m) 782 } 783 lastApplied, err = getLastApplied(f.Live()) 784 if err != nil { 785 t.Errorf("failed to get last applied: %v", err) 786 } 787 if !strings.Contains(lastApplied, "my-app-v2") { 788 t.Errorf("expected last applied annotation to be updated, but got: %q", lastApplied) 789 } 790 } 791 792 func yamlToJSON(y []byte) (string, error) { 793 obj := &unstructured.Unstructured{Object: map[string]interface{}{}} 794 if err := yaml.Unmarshal(y, &obj.Object); err != nil { 795 return "", fmt.Errorf("error decoding YAML: %v", err) 796 } 797 serialization, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj) 798 if err != nil { 799 return "", fmt.Errorf("error encoding object: %v", err) 800 } 801 json, err := yamlutil.ToJSON(serialization) 802 if err != nil { 803 return "", fmt.Errorf("error converting to json: %v", err) 804 } 805 return string(json), nil 806 } 807 808 func setLastAppliedFromEncoded(obj runtime.Object, lastApplied []byte) error { 809 lastAppliedJSON, err := yamlToJSON(lastApplied) 810 if err != nil { 811 return err 812 } 813 return internal.SetLastApplied(obj, lastAppliedJSON) 814 } 815 816 func getLastApplied(obj runtime.Object) (string, error) { 817 accessor := meta.NewAccessor() 818 annotations, err := accessor.Annotations(obj) 819 if err != nil { 820 return "", fmt.Errorf("failed to access annotations: %v", err) 821 } 822 if annotations == nil { 823 return "", fmt.Errorf("no annotations on obj: %v", obj) 824 } 825 826 lastApplied, ok := annotations[internal.LastAppliedConfigAnnotation] 827 if !ok { 828 return "", fmt.Errorf("expected last applied annotation, but got none for object: %v", obj) 829 } 830 return lastApplied, nil 831 } 832 833 func TestUpdateViaSubresources(t *testing.T) { 834 f := managedfieldstest.NewTestFieldManagerSubresource(fakeTypeConverter, schema.FromAPIVersionAndKind("v1", "Pod"), "scale") 835 836 obj := &unstructured.Unstructured{Object: map[string]interface{}{}} 837 if err := yaml.Unmarshal([]byte(`{ 838 "apiVersion": "v1", 839 "kind": "Pod", 840 "metadata": { 841 "labels": { 842 "a":"b" 843 }, 844 } 845 }`), &obj.Object); err != nil { 846 t.Fatalf("error decoding YAML: %v", err) 847 } 848 obj.SetManagedFields([]metav1.ManagedFieldsEntry{ 849 { 850 Manager: "test", 851 Operation: metav1.ManagedFieldsOperationApply, 852 APIVersion: "apps/v1", 853 FieldsType: "FieldsV1", 854 FieldsV1: &metav1.FieldsV1{ 855 Raw: []byte(`{"f:metadata":{"f:labels":{"f:another_field":{}}}}`), 856 }, 857 }, 858 }) 859 860 // Check that managed fields cannot be changed explicitly via subresources 861 expectedManager := "fieldmanager_test_subresource" 862 if err := f.Update(obj, expectedManager); err != nil { 863 t.Fatalf("failed to apply object: %v", err) 864 } 865 866 managedFields := f.ManagedFields() 867 if len(managedFields) != 1 { 868 t.Fatalf("Expected new managed fields to have one entry. Got:\n%#v", managedFields) 869 } 870 if managedFields[0].Manager != expectedManager { 871 t.Fatalf("Expected first item to have manager set to: %s. Got: %s", expectedManager, managedFields[0].Manager) 872 } 873 874 // Check that managed fields cannot be reset via subresources 875 newObj := obj.DeepCopy() 876 newObj.SetManagedFields([]metav1.ManagedFieldsEntry{}) 877 if err := f.Update(newObj, expectedManager); err != nil { 878 t.Fatalf("failed to apply object: %v", err) 879 } 880 newManagedFields := f.ManagedFields() 881 if len(newManagedFields) != 1 { 882 t.Fatalf("Expected new managed fields to have one entry. Got:\n%#v", newManagedFields) 883 } 884 } 885 886 // Ensures that a no-op Apply does not mutate managed fields 887 func TestApplyDoesNotChangeManagedFields(t *testing.T) { 888 originalManagedFields := []metav1.ManagedFieldsEntry{} 889 f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, 890 schema.FromAPIVersionAndKind("apps/v1", "Deployment")) 891 newObj := &unstructured.Unstructured{ 892 Object: map[string]interface{}{}, 893 } 894 appliedObj := &unstructured.Unstructured{ 895 Object: map[string]interface{}{}, 896 } 897 898 // Convert YAML string inputs to unstructured instances 899 if err := yaml.Unmarshal([]byte(`{ 900 "apiVersion": "apps/v1", 901 "kind": "Deployment", 902 "metadata": { 903 "name": "deployment", 904 "labels": {"app": "nginx"} 905 }, 906 "spec": { 907 "selector": { 908 "matchLabels": { 909 "app": "nginx" 910 } 911 }, 912 "template": { 913 "metadata": { 914 "labels": { 915 "app": "nginx" 916 } 917 }, 918 "spec": { 919 "containers": [{ 920 "name": "nginx", 921 "image": "nginx:latest" 922 }] 923 } 924 } 925 } 926 }`), &newObj.Object); err != nil { 927 t.Fatalf("error decoding YAML: %v", err) 928 } 929 930 if err := yaml.Unmarshal([]byte(`{ 931 "apiVersion": "apps/v1", 932 "kind": "Deployment", 933 "metadata": { 934 "name": "deployment", 935 }, 936 "spec": { 937 "replicas": 101, 938 } 939 }`), &appliedObj.Object); err != nil { 940 t.Fatalf("error decoding YAML: %v", err) 941 } 942 943 // Agent A applies initial configuration 944 if err := f.Apply(newObj.DeepCopyObject(), "fieldmanager_z", false); err != nil { 945 t.Fatalf("failed to apply object: %v", err) 946 } 947 948 // Agent B applies additive configuration 949 if err := f.Apply(appliedObj, "fieldmanager_b", false); err != nil { 950 t.Fatalf("failed to apply object %v", err) 951 } 952 953 // Next, agent A applies the initial configuration again, but we expect 954 // a no-op to managed fields. 955 // 956 // The following update is expected not to change the liveObj, save off 957 // the fields 958 for _, field := range f.ManagedFields() { 959 originalManagedFields = append(originalManagedFields, *field.DeepCopy()) 960 } 961 962 // Make sure timestamp change would be caught 963 time.Sleep(2 * time.Second) 964 965 if err := f.Apply(newObj, "fieldmanager_z", false); err != nil { 966 t.Fatalf("failed to apply object: %v", err) 967 } 968 969 // ensure that the live object is unchanged 970 if !reflect.DeepEqual(originalManagedFields, f.ManagedFields()) { 971 originalYAML, _ := yaml.Marshal(originalManagedFields) 972 current, _ := yaml.Marshal(f.ManagedFields()) 973 974 // should have been a no-op w.r.t. managed fields 975 t.Fatalf("managed fields changed: ORIGINAL\n%v\nCURRENT\n%v", 976 string(originalYAML), string(current)) 977 } 978 } 979 980 // Ensures that a no-op Update does not mutate managed fields 981 func TestUpdateDoesNotChangeManagedFields(t *testing.T) { 982 originalManagedFields := []metav1.ManagedFieldsEntry{} 983 f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, 984 schema.FromAPIVersionAndKind("apps/v1", "Deployment")) 985 newObj := &unstructured.Unstructured{ 986 Object: map[string]interface{}{}, 987 } 988 989 if err := yaml.Unmarshal([]byte(`{ 990 "apiVersion": "apps/v1", 991 "kind": "Deployment", 992 "metadata": { 993 "name": "deployment", 994 "labels": {"app": "nginx"} 995 }, 996 "spec": { 997 "selector": { 998 "matchLabels": { 999 "app": "nginx" 1000 } 1001 }, 1002 "template": { 1003 "metadata": { 1004 "labels": { 1005 "app": "nginx" 1006 } 1007 }, 1008 "spec": { 1009 "containers": [{ 1010 "name": "nginx", 1011 "image": "nginx:latest" 1012 }] 1013 } 1014 } 1015 } 1016 }`), &newObj.Object); err != nil { 1017 t.Fatalf("error decoding YAML: %v", err) 1018 } 1019 1020 // Agent A updates with initial configuration 1021 if err := f.Update(newObj.DeepCopyObject(), "fieldmanager_z"); err != nil { 1022 t.Fatalf("failed to apply object: %v", err) 1023 } 1024 1025 for _, field := range f.ManagedFields() { 1026 originalManagedFields = append(originalManagedFields, *field.DeepCopy()) 1027 } 1028 1029 // Make sure timestamp change would be caught 1030 time.Sleep(2 * time.Second) 1031 1032 // If the same exact configuration is updated once again, the 1033 // managed fields are not expected to change 1034 // 1035 // However, a change in field ownership WOULD be a semantic change which 1036 // should cause managed fields to change. 1037 if err := f.Update(newObj, "fieldmanager_z"); err != nil { 1038 t.Fatalf("failed to apply object: %v", err) 1039 } 1040 1041 // ensure that the live object is unchanged 1042 if !reflect.DeepEqual(originalManagedFields, f.ManagedFields()) { 1043 originalYAML, _ := yaml.Marshal(originalManagedFields) 1044 current, _ := yaml.Marshal(f.ManagedFields()) 1045 1046 // should have been a no-op w.r.t. managed fields 1047 t.Fatalf("managed fields changed: ORIGINAL\n%v\nCURRENT\n%v", 1048 string(originalYAML), string(current)) 1049 } 1050 } 1051 1052 // This test makes sure that the liveObject during a patch does not mutate 1053 // its managed fields. 1054 func TestLiveObjectManagedFieldsNotRemoved(t *testing.T) { 1055 f := managedfieldstest.NewTestFieldManager(fakeTypeConverter, 1056 schema.FromAPIVersionAndKind("apps/v1", "Deployment")) 1057 newObj := &unstructured.Unstructured{ 1058 Object: map[string]interface{}{}, 1059 } 1060 appliedObj := &unstructured.Unstructured{ 1061 Object: map[string]interface{}{}, 1062 } 1063 // Convert YAML string inputs to unstructured instances 1064 if err := yaml.Unmarshal([]byte(`{ 1065 "apiVersion": "apps/v1", 1066 "kind": "Deployment", 1067 "metadata": { 1068 "name": "deployment", 1069 "labels": {"app": "nginx"} 1070 }, 1071 "spec": { 1072 "selector": { 1073 "matchLabels": { 1074 "app": "nginx" 1075 } 1076 }, 1077 "template": { 1078 "metadata": { 1079 "labels": { 1080 "app": "nginx" 1081 } 1082 }, 1083 "spec": { 1084 "containers": [{ 1085 "name": "nginx", 1086 "image": "nginx:latest" 1087 }] 1088 } 1089 } 1090 } 1091 }`), &newObj.Object); err != nil { 1092 t.Fatalf("error decoding YAML: %v", err) 1093 } 1094 1095 if err := yaml.Unmarshal([]byte(`{ 1096 "apiVersion": "apps/v1", 1097 "kind": "Deployment", 1098 "metadata": { 1099 "name": "deployment", 1100 }, 1101 "spec": { 1102 "replicas": 101, 1103 } 1104 }`), &appliedObj.Object); err != nil { 1105 t.Fatalf("error decoding YAML: %v", err) 1106 } 1107 1108 // Agent A applies initial configuration 1109 if err := f.Apply(newObj.DeepCopyObject(), "fieldmanager_z", false); err != nil { 1110 t.Fatalf("failed to apply object: %v", err) 1111 } 1112 1113 originalLiveObj := f.Live() 1114 1115 accessor, err := meta.Accessor(originalLiveObj) 1116 if err != nil { 1117 panic(fmt.Errorf("couldn't get accessor: %v", err)) 1118 } 1119 1120 // Managed fields should not be stripped 1121 if len(accessor.GetManagedFields()) == 0 { 1122 t.Fatalf("empty managed fields of object which expected nonzero fields") 1123 } 1124 1125 // Agent A applies the exact same configuration 1126 if err := f.Apply(appliedObj.DeepCopyObject(), "fieldmanager_z", false); err != nil { 1127 t.Fatalf("failed to apply object: %v", err) 1128 } 1129 1130 accessor, err = meta.Accessor(originalLiveObj) 1131 if err != nil { 1132 panic(fmt.Errorf("couldn't get accessor: %v", err)) 1133 } 1134 1135 // Managed fields should not be stripped 1136 if len(accessor.GetManagedFields()) == 0 { 1137 t.Fatalf("empty managed fields of object which expected nonzero fields") 1138 } 1139 }