k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/registry/core/pod/storage/storage_test.go (about) 1 /* 2 Copyright 2014 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 storage 18 19 import ( 20 "context" 21 goerrors "errors" 22 "fmt" 23 "net/url" 24 "strings" 25 "testing" 26 "time" 27 28 "github.com/google/go-cmp/cmp" 29 v1 "k8s.io/api/core/v1" 30 apiequality "k8s.io/apimachinery/pkg/api/equality" 31 "k8s.io/apimachinery/pkg/api/errors" 32 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 33 "k8s.io/apimachinery/pkg/fields" 34 "k8s.io/apimachinery/pkg/labels" 35 "k8s.io/apimachinery/pkg/runtime" 36 "k8s.io/apimachinery/pkg/types" 37 genericapirequest "k8s.io/apiserver/pkg/endpoints/request" 38 "k8s.io/apiserver/pkg/registry/generic" 39 genericregistry "k8s.io/apiserver/pkg/registry/generic/registry" 40 genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing" 41 "k8s.io/apiserver/pkg/registry/rest" 42 apiserverstorage "k8s.io/apiserver/pkg/storage" 43 storeerr "k8s.io/apiserver/pkg/storage/errors" 44 etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing" 45 api "k8s.io/kubernetes/pkg/apis/core" 46 "k8s.io/kubernetes/pkg/registry/registrytest" 47 "k8s.io/kubernetes/pkg/securitycontext" 48 ) 49 50 func newStorage(t *testing.T) (*REST, *BindingREST, *StatusREST, *etcd3testing.EtcdTestServer) { 51 etcdStorage, server := registrytest.NewEtcdStorage(t, "") 52 restOptions := generic.RESTOptions{ 53 StorageConfig: etcdStorage, 54 Decorator: generic.UndecoratedStorage, 55 DeleteCollectionWorkers: 3, 56 ResourcePrefix: "pods", 57 } 58 storage, err := NewStorage(restOptions, nil, nil, nil) 59 if err != nil { 60 t.Fatalf("unexpected error from REST storage: %v", err) 61 } 62 return storage.Pod, storage.Binding, storage.Status, server 63 } 64 65 func validNewPod() *api.Pod { 66 grace := int64(30) 67 enableServiceLinks := v1.DefaultEnableServiceLinks 68 return &api.Pod{ 69 ObjectMeta: metav1.ObjectMeta{ 70 Name: "foo", 71 Namespace: metav1.NamespaceDefault, 72 }, 73 Spec: api.PodSpec{ 74 RestartPolicy: api.RestartPolicyAlways, 75 DNSPolicy: api.DNSClusterFirst, 76 77 TerminationGracePeriodSeconds: &grace, 78 Containers: []api.Container{ 79 { 80 Name: "foo", 81 Image: "test", 82 ImagePullPolicy: api.PullAlways, 83 84 TerminationMessagePath: api.TerminationMessagePathDefault, 85 TerminationMessagePolicy: api.TerminationMessageReadFile, 86 SecurityContext: securitycontext.ValidInternalSecurityContextWithContainerDefaults(), 87 }, 88 }, 89 SecurityContext: &api.PodSecurityContext{}, 90 SchedulerName: v1.DefaultSchedulerName, 91 EnableServiceLinks: &enableServiceLinks, 92 }, 93 } 94 } 95 96 func validChangedPod() *api.Pod { 97 pod := validNewPod() 98 pod.Labels = map[string]string{ 99 "foo": "bar", 100 } 101 return pod 102 } 103 104 func TestCreate(t *testing.T) { 105 storage, _, _, server := newStorage(t) 106 defer server.Terminate(t) 107 defer storage.Store.DestroyFunc() 108 test := genericregistrytest.New(t, storage.Store) 109 pod := validNewPod() 110 pod.ObjectMeta = metav1.ObjectMeta{} 111 // Make an invalid pod with an incorrect label. 112 invalidPod := validNewPod() 113 invalidPod.Namespace = test.TestNamespace() 114 invalidPod.Labels = map[string]string{ 115 "invalid/label/to/cause/validation/failure": "bar", 116 } 117 test.TestCreate( 118 // valid 119 pod, 120 // invalid (empty contains list) 121 &api.Pod{ 122 Spec: api.PodSpec{ 123 Containers: []api.Container{}, 124 }, 125 }, 126 // invalid (invalid labels) 127 invalidPod, 128 ) 129 } 130 131 func TestUpdate(t *testing.T) { 132 storage, _, _, server := newStorage(t) 133 defer server.Terminate(t) 134 defer storage.Store.DestroyFunc() 135 test := genericregistrytest.New(t, storage.Store) 136 test.TestUpdate( 137 // valid 138 validNewPod(), 139 // updateFunc 140 func(obj runtime.Object) runtime.Object { 141 object := obj.(*api.Pod) 142 object.Labels = map[string]string{"a": "b"} 143 return object 144 }, 145 ) 146 } 147 148 func TestDelete(t *testing.T) { 149 storage, _, _, server := newStorage(t) 150 defer server.Terminate(t) 151 defer storage.Store.DestroyFunc() 152 test := genericregistrytest.New(t, storage.Store).ReturnDeletedObject() 153 test.TestDelete(validNewPod()) 154 155 scheduledPod := validNewPod() 156 scheduledPod.Spec.NodeName = "some-node" 157 test.TestDeleteGraceful(scheduledPod, 30) 158 } 159 160 type FailDeletionStorage struct { 161 apiserverstorage.Interface 162 Called *bool 163 } 164 165 func (f FailDeletionStorage) Delete(_ context.Context, key string, _ runtime.Object, _ *apiserverstorage.Preconditions, _ apiserverstorage.ValidateObjectFunc, _ runtime.Object) error { 166 *f.Called = true 167 return apiserverstorage.NewKeyNotFoundError(key, 0) 168 } 169 170 func newFailDeleteStorage(t *testing.T, called *bool) (*REST, *etcd3testing.EtcdTestServer) { 171 etcdStorage, server := registrytest.NewEtcdStorage(t, "") 172 restOptions := generic.RESTOptions{ 173 StorageConfig: etcdStorage, 174 Decorator: generic.UndecoratedStorage, 175 DeleteCollectionWorkers: 3, 176 ResourcePrefix: "pods", 177 } 178 storage, err := NewStorage(restOptions, nil, nil, nil) 179 if err != nil { 180 t.Fatalf("unexpected error from REST storage: %v", err) 181 } 182 storage.Pod.Store.Storage = genericregistry.DryRunnableStorage{Storage: FailDeletionStorage{storage.Pod.Store.Storage.Storage, called}} 183 return storage.Pod, server 184 } 185 186 func TestIgnoreDeleteNotFound(t *testing.T) { 187 pod := validNewPod() 188 testContext := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault) 189 called := false 190 registry, server := newFailDeleteStorage(t, &called) 191 defer server.Terminate(t) 192 defer registry.Store.DestroyFunc() 193 194 // should fail if pod A is not created yet. 195 _, _, err := registry.Delete(testContext, pod.Name, rest.ValidateAllObjectFunc, nil) 196 if !errors.IsNotFound(err) { 197 t.Errorf("Unexpected error: %v", err) 198 } 199 200 // create pod 201 _, err = registry.Create(testContext, pod, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 202 if err != nil { 203 t.Errorf("Unexpected error: %v", err) 204 } 205 206 // delete object with grace period 0, storage will return NotFound, but the 207 // registry shouldn't get any error since we ignore the NotFound error. 208 zero := int64(0) 209 opt := &metav1.DeleteOptions{GracePeriodSeconds: &zero} 210 obj, _, err := registry.Delete(testContext, pod.Name, rest.ValidateAllObjectFunc, opt) 211 if err != nil { 212 t.Fatalf("Unexpected error: %v", err) 213 } 214 215 if !called { 216 t.Fatalf("expect the overriding Delete method to be called") 217 } 218 deletedPod, ok := obj.(*api.Pod) 219 if !ok { 220 t.Fatalf("expect a pod is returned") 221 } 222 if deletedPod.DeletionTimestamp == nil { 223 t.Errorf("expect the DeletionTimestamp to be set") 224 } 225 if deletedPod.DeletionGracePeriodSeconds == nil { 226 t.Fatalf("expect the DeletionGracePeriodSeconds to be set") 227 } 228 if *deletedPod.DeletionGracePeriodSeconds != 0 { 229 t.Errorf("expect the DeletionGracePeriodSeconds to be 0, got %v", *deletedPod.DeletionTimestamp) 230 } 231 } 232 233 func TestCreateSetsFields(t *testing.T) { 234 storage, _, _, server := newStorage(t) 235 defer server.Terminate(t) 236 defer storage.Store.DestroyFunc() 237 pod := validNewPod() 238 _, err := storage.Create(genericapirequest.NewDefaultContext(), pod, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 239 if err != nil { 240 t.Fatalf("unexpected error: %v", err) 241 } 242 ctx := genericapirequest.NewDefaultContext() 243 object, err := storage.Get(ctx, "foo", &metav1.GetOptions{}) 244 if err != nil { 245 t.Errorf("unexpected error: %v", err) 246 } 247 actual := object.(*api.Pod) 248 if actual.Name != pod.Name { 249 t.Errorf("unexpected pod: %#v", actual) 250 } 251 if len(actual.UID) == 0 { 252 t.Errorf("expected pod UID to be set: %#v", actual) 253 } 254 } 255 256 func TestResourceLocation(t *testing.T) { 257 expectedIP := "1.2.3.4" 258 expectedIP6 := "fd00:10:244:0:2::6b" 259 testCases := []struct { 260 pod api.Pod 261 query string 262 location string 263 }{ 264 { 265 pod: api.Pod{ 266 ObjectMeta: metav1.ObjectMeta{Name: "foo"}, 267 Status: api.PodStatus{PodIPs: []api.PodIP{{IP: expectedIP}}}, 268 }, 269 query: "foo", 270 location: expectedIP, 271 }, 272 { 273 pod: api.Pod{ 274 ObjectMeta: metav1.ObjectMeta{Name: "foo"}, 275 Status: api.PodStatus{PodIPs: []api.PodIP{{IP: expectedIP}}}, 276 }, 277 query: "foo:12345", 278 location: expectedIP + ":12345", 279 }, 280 { 281 pod: api.Pod{ 282 ObjectMeta: metav1.ObjectMeta{Name: "foo"}, 283 Spec: api.PodSpec{ 284 Containers: []api.Container{ 285 {Name: "ctr"}, 286 }, 287 }, 288 Status: api.PodStatus{PodIPs: []api.PodIP{{IP: expectedIP}}}, 289 }, 290 query: "foo", 291 location: expectedIP, 292 }, 293 { 294 pod: api.Pod{ 295 ObjectMeta: metav1.ObjectMeta{Name: "foo"}, 296 Spec: api.PodSpec{ 297 Containers: []api.Container{ 298 {Name: "ctr"}, 299 }, 300 }, 301 Status: api.PodStatus{PodIPs: []api.PodIP{{IP: expectedIP6}}}, 302 }, 303 query: "foo", 304 location: "[" + expectedIP6 + "]", 305 }, 306 { 307 pod: api.Pod{ 308 ObjectMeta: metav1.ObjectMeta{Name: "foo"}, 309 Spec: api.PodSpec{ 310 Containers: []api.Container{ 311 {Name: "ctr", Ports: []api.ContainerPort{{ContainerPort: 9376}}}, 312 }, 313 }, 314 Status: api.PodStatus{PodIPs: []api.PodIP{{IP: expectedIP}}}, 315 }, 316 query: "foo", 317 location: expectedIP + ":9376", 318 }, 319 { 320 pod: api.Pod{ 321 ObjectMeta: metav1.ObjectMeta{Name: "foo"}, 322 Spec: api.PodSpec{ 323 Containers: []api.Container{ 324 {Name: "ctr", Ports: []api.ContainerPort{{ContainerPort: 9376}}}, 325 }, 326 }, 327 Status: api.PodStatus{PodIPs: []api.PodIP{{IP: expectedIP}}}, 328 }, 329 query: "foo:12345", 330 location: expectedIP + ":12345", 331 }, 332 { 333 pod: api.Pod{ 334 ObjectMeta: metav1.ObjectMeta{Name: "foo"}, 335 Spec: api.PodSpec{ 336 Containers: []api.Container{ 337 {Name: "ctr1"}, 338 {Name: "ctr2", Ports: []api.ContainerPort{{ContainerPort: 9376}}}, 339 }, 340 }, 341 Status: api.PodStatus{PodIPs: []api.PodIP{{IP: expectedIP}}}, 342 }, 343 query: "foo", 344 location: expectedIP + ":9376", 345 }, 346 { 347 pod: api.Pod{ 348 ObjectMeta: metav1.ObjectMeta{Name: "foo"}, 349 Spec: api.PodSpec{ 350 Containers: []api.Container{ 351 {Name: "ctr1", Ports: []api.ContainerPort{{ContainerPort: 9376}}}, 352 {Name: "ctr2", Ports: []api.ContainerPort{{ContainerPort: 1234}}}, 353 }, 354 }, 355 Status: api.PodStatus{PodIPs: []api.PodIP{{IP: expectedIP}}}, 356 }, 357 query: "foo", 358 location: expectedIP + ":9376", 359 }, 360 { 361 pod: api.Pod{ 362 ObjectMeta: metav1.ObjectMeta{Name: "foo"}, 363 Spec: api.PodSpec{ 364 Containers: []api.Container{ 365 {Name: "ctr1", Ports: []api.ContainerPort{{ContainerPort: 9376}}}, 366 {Name: "ctr2", Ports: []api.ContainerPort{{ContainerPort: 1234}}}, 367 }, 368 }, 369 Status: api.PodStatus{PodIPs: []api.PodIP{{IP: expectedIP}, {IP: expectedIP6}}}, 370 }, 371 query: "foo", 372 location: expectedIP + ":9376", 373 }, 374 { 375 pod: api.Pod{ 376 ObjectMeta: metav1.ObjectMeta{Name: "foo"}, 377 Spec: api.PodSpec{ 378 Containers: []api.Container{ 379 {Name: "ctr1", Ports: []api.ContainerPort{{ContainerPort: 9376}}}, 380 {Name: "ctr2", Ports: []api.ContainerPort{{ContainerPort: 1234}}}, 381 }, 382 }, 383 Status: api.PodStatus{PodIPs: []api.PodIP{{IP: expectedIP6}, {IP: expectedIP}}}, 384 }, 385 query: "foo", 386 location: "[" + expectedIP6 + "]:9376", 387 }, 388 } 389 390 storage, _, _, server := newStorage(t) 391 defer server.Terminate(t) 392 for i, tc := range testCases { 393 // unique namespace/storage location per test 394 ctx := genericapirequest.WithNamespace(genericapirequest.NewDefaultContext(), fmt.Sprintf("namespace-%d", i)) 395 key, _ := storage.KeyFunc(ctx, tc.pod.Name) 396 if err := storage.Storage.Create(ctx, key, &tc.pod, nil, 0, false); err != nil { 397 t.Fatalf("unexpected error: %v", err) 398 } 399 400 redirector := rest.Redirector(storage) 401 location, _, err := redirector.ResourceLocation(ctx, tc.query) 402 if err != nil { 403 t.Errorf("Unexpected error: %v", err) 404 } 405 if location == nil { 406 t.Errorf("Unexpected nil: %v", location) 407 } 408 409 if location.Scheme != "" { 410 t.Errorf("Expected '%v', but got '%v'", "", location.Scheme) 411 } 412 if location.Host != tc.location { 413 t.Errorf("Expected %v, but got %v", tc.location, location.Host) 414 } 415 if _, err := url.Parse(location.String()); err != nil { 416 t.Errorf("could not parse returned location %s: %v", location.String(), err) 417 } 418 419 } 420 } 421 422 func TestGet(t *testing.T) { 423 storage, _, _, server := newStorage(t) 424 defer server.Terminate(t) 425 defer storage.Store.DestroyFunc() 426 test := genericregistrytest.New(t, storage.Store) 427 test.TestGet(validNewPod()) 428 } 429 430 func TestList(t *testing.T) { 431 storage, _, _, server := newStorage(t) 432 defer server.Terminate(t) 433 defer storage.Store.DestroyFunc() 434 test := genericregistrytest.New(t, storage.Store) 435 test.TestList(validNewPod()) 436 } 437 438 func TestWatch(t *testing.T) { 439 storage, _, _, server := newStorage(t) 440 defer server.Terminate(t) 441 defer storage.Store.DestroyFunc() 442 test := genericregistrytest.New(t, storage.Store) 443 test.TestWatch( 444 validNewPod(), 445 // matching labels 446 []labels.Set{}, 447 // not matching labels 448 []labels.Set{ 449 {"foo": "bar"}, 450 }, 451 // matching fields 452 []fields.Set{ 453 {"metadata.name": "foo"}, 454 }, 455 // not matching fields 456 []fields.Set{ 457 {"metadata.name": "bar"}, 458 }, 459 ) 460 } 461 462 func TestConvertToTableList(t *testing.T) { 463 storage, _, _, server := newStorage(t) 464 defer server.Terminate(t) 465 defer storage.Store.DestroyFunc() 466 ctx := genericapirequest.NewDefaultContext() 467 468 columns := []metav1.TableColumnDefinition{ 469 {Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]}, 470 {Name: "Ready", Type: "string", Description: "The aggregate readiness state of this pod for accepting traffic."}, 471 {Name: "Status", Type: "string", Description: "The aggregate status of the containers in this pod."}, 472 {Name: "Restarts", Type: "string", Description: "The number of times the containers in this pod have been restarted and when the last container in this pod has restarted."}, 473 {Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]}, 474 {Name: "IP", Type: "string", Priority: 1, Description: v1.PodStatus{}.SwaggerDoc()["podIP"]}, 475 {Name: "Node", Type: "string", Priority: 1, Description: v1.PodSpec{}.SwaggerDoc()["nodeName"]}, 476 {Name: "Nominated Node", Type: "string", Priority: 1, Description: v1.PodStatus{}.SwaggerDoc()["nominatedNodeName"]}, 477 {Name: "Readiness Gates", Type: "string", Priority: 1, Description: v1.PodSpec{}.SwaggerDoc()["readinessGates"]}, 478 } 479 480 condition1 := "condition1" 481 condition2 := "condition2" 482 pod1 := &api.Pod{ 483 ObjectMeta: metav1.ObjectMeta{Namespace: "test", Name: "foo", CreationTimestamp: metav1.NewTime(time.Now().Add(-370 * 24 * time.Hour))}, 484 Spec: api.PodSpec{ 485 Containers: []api.Container{ 486 {Name: "ctr1"}, 487 {Name: "ctr2", Ports: []api.ContainerPort{{ContainerPort: 9376}}}, 488 }, 489 NodeName: "test-node", 490 ReadinessGates: []api.PodReadinessGate{ 491 { 492 ConditionType: api.PodConditionType(condition1), 493 }, 494 { 495 ConditionType: api.PodConditionType(condition2), 496 }, 497 }, 498 }, 499 Status: api.PodStatus{ 500 Conditions: []api.PodCondition{ 501 { 502 Type: api.PodConditionType(condition1), 503 Status: api.ConditionFalse, 504 }, 505 { 506 Type: api.PodConditionType(condition2), 507 Status: api.ConditionTrue, 508 }, 509 }, 510 PodIPs: []api.PodIP{{IP: "10.1.2.3"}}, 511 Phase: api.PodPending, 512 ContainerStatuses: []api.ContainerStatus{ 513 {Name: "ctr1", State: api.ContainerState{Running: &api.ContainerStateRunning{}}, RestartCount: 10, Ready: true}, 514 {Name: "ctr2", State: api.ContainerState{Waiting: &api.ContainerStateWaiting{}}, RestartCount: 0}, 515 }, 516 NominatedNodeName: "nominated-node", 517 }, 518 } 519 520 multiIPsPod := &api.Pod{ 521 ObjectMeta: metav1.ObjectMeta{Namespace: "test", Name: "foo", CreationTimestamp: metav1.NewTime(time.Now().Add(-370 * 24 * time.Hour))}, 522 Spec: api.PodSpec{ 523 Containers: []api.Container{ 524 {Name: "ctr1"}, 525 {Name: "ctr2", Ports: []api.ContainerPort{{ContainerPort: 9376}}}, 526 }, 527 NodeName: "test-node", 528 ReadinessGates: []api.PodReadinessGate{ 529 { 530 ConditionType: api.PodConditionType(condition1), 531 }, 532 { 533 ConditionType: api.PodConditionType(condition2), 534 }, 535 }, 536 }, 537 Status: api.PodStatus{ 538 Conditions: []api.PodCondition{ 539 { 540 Type: api.PodConditionType(condition1), 541 Status: api.ConditionFalse, 542 }, 543 { 544 Type: api.PodConditionType(condition2), 545 Status: api.ConditionTrue, 546 }, 547 }, 548 PodIPs: []api.PodIP{{IP: "10.1.2.3"}, {IP: "2001:db8::"}}, 549 Phase: api.PodPending, 550 ContainerStatuses: []api.ContainerStatus{ 551 {Name: "ctr1", State: api.ContainerState{Running: &api.ContainerStateRunning{}}, RestartCount: 10, Ready: true}, 552 {Name: "ctr2", State: api.ContainerState{Waiting: &api.ContainerStateWaiting{}}, RestartCount: 0}, 553 }, 554 NominatedNodeName: "nominated-node", 555 }, 556 } 557 558 testCases := []struct { 559 in runtime.Object 560 out *metav1.Table 561 err bool 562 }{ 563 { 564 in: nil, 565 err: true, 566 }, 567 { 568 in: &api.Pod{}, 569 out: &metav1.Table{ 570 ColumnDefinitions: columns, 571 Rows: []metav1.TableRow{ 572 {Cells: []interface{}{"", "0/0", "", "0", "<unknown>", "<none>", "<none>", "<none>", "<none>"}, Object: runtime.RawExtension{Object: &api.Pod{}}}, 573 }, 574 }, 575 }, 576 { 577 in: pod1, 578 out: &metav1.Table{ 579 ColumnDefinitions: columns, 580 Rows: []metav1.TableRow{ 581 {Cells: []interface{}{"foo", "1/2", "Pending", "10", "370d", "10.1.2.3", "test-node", "nominated-node", "1/2"}, Object: runtime.RawExtension{Object: pod1}}, 582 }, 583 }, 584 }, 585 { 586 in: &api.PodList{}, 587 out: &metav1.Table{ColumnDefinitions: columns}, 588 }, 589 { 590 in: multiIPsPod, 591 out: &metav1.Table{ 592 ColumnDefinitions: columns, 593 Rows: []metav1.TableRow{ 594 {Cells: []interface{}{"foo", "1/2", "Pending", "10", "370d", "10.1.2.3", "test-node", "nominated-node", "1/2"}, Object: runtime.RawExtension{Object: multiIPsPod}}, 595 }, 596 }, 597 }, 598 } 599 for i, test := range testCases { 600 out, err := storage.ConvertToTable(ctx, test.in, nil) 601 if err != nil { 602 if test.err { 603 continue 604 } 605 t.Errorf("%d: error: %v", i, err) 606 continue 607 } 608 if !apiequality.Semantic.DeepEqual(test.out, out) { 609 t.Errorf("%d: mismatch: %s", i, cmp.Diff(test.out, out)) 610 } 611 } 612 } 613 614 func TestEtcdCreate(t *testing.T) { 615 storage, bindingStorage, _, server := newStorage(t) 616 defer server.Terminate(t) 617 defer storage.Store.DestroyFunc() 618 ctx := genericapirequest.NewDefaultContext() 619 _, err := storage.Create(ctx, validNewPod(), rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 620 if err != nil { 621 t.Fatalf("unexpected error: %v", err) 622 } 623 624 // Suddenly, a wild scheduler appears: 625 _, err = bindingStorage.Create(ctx, "foo", &api.Binding{ 626 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "foo"}, 627 Target: api.ObjectReference{Name: "machine"}, 628 }, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 629 if err != nil { 630 t.Fatalf("unexpected error: %v", err) 631 } 632 633 _, err = storage.Get(ctx, "foo", &metav1.GetOptions{}) 634 if err != nil { 635 t.Fatalf("Unexpected error %v", err) 636 } 637 } 638 639 // Ensure that when scheduler creates a binding for a pod that has already been deleted 640 // by the API server, API server returns not-found error. 641 func TestEtcdCreateBindingNoPod(t *testing.T) { 642 storage, bindingStorage, _, server := newStorage(t) 643 defer server.Terminate(t) 644 defer storage.Store.DestroyFunc() 645 ctx := genericapirequest.NewDefaultContext() 646 647 // Assume that a pod has undergone the following: 648 // - Create (apiserver) 649 // - Schedule (scheduler) 650 // - Delete (apiserver) 651 _, err := bindingStorage.Create(ctx, "foo", &api.Binding{ 652 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "foo"}, 653 Target: api.ObjectReference{Name: "machine"}, 654 }, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 655 if err == nil { 656 t.Fatalf("Expected not-found-error but got nothing") 657 } 658 if !errors.IsNotFound(storeerr.InterpretGetError(err, api.Resource("pods"), "foo")) { 659 t.Fatalf("Unexpected error returned: %#v", err) 660 } 661 662 _, err = storage.Get(ctx, "foo", &metav1.GetOptions{}) 663 if err == nil { 664 t.Fatalf("Expected not-found-error but got nothing") 665 } 666 if !errors.IsNotFound(storeerr.InterpretGetError(err, api.Resource("pods"), "foo")) { 667 t.Fatalf("Unexpected error: %v", err) 668 } 669 } 670 671 func TestEtcdCreateFailsWithoutNamespace(t *testing.T) { 672 storage, _, _, server := newStorage(t) 673 defer server.Terminate(t) 674 defer storage.Store.DestroyFunc() 675 pod := validNewPod() 676 pod.Namespace = "" 677 _, err := storage.Create(genericapirequest.NewContext(), pod, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 678 // Accept "namespace" or "Namespace". 679 if err == nil || !strings.Contains(err.Error(), "amespace") { 680 t.Fatalf("expected error that namespace was missing from context, got: %v", err) 681 } 682 } 683 684 func TestEtcdCreateWithContainersNotFound(t *testing.T) { 685 storage, bindingStorage, _, server := newStorage(t) 686 defer server.Terminate(t) 687 defer storage.Store.DestroyFunc() 688 ctx := genericapirequest.NewDefaultContext() 689 _, err := storage.Create(ctx, validNewPod(), rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 690 if err != nil { 691 t.Fatalf("unexpected error: %v", err) 692 } 693 694 // Suddenly, a wild scheduler appears: 695 _, err = bindingStorage.Create(ctx, "foo", &api.Binding{ 696 ObjectMeta: metav1.ObjectMeta{ 697 Namespace: metav1.NamespaceDefault, 698 Name: "foo", 699 Annotations: map[string]string{"label1": "value1"}, 700 }, 701 Target: api.ObjectReference{Name: "machine"}, 702 }, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 703 if err != nil { 704 t.Fatalf("unexpected error: %v", err) 705 } 706 707 obj, err := storage.Get(ctx, "foo", &metav1.GetOptions{}) 708 if err != nil { 709 t.Fatalf("Unexpected error %v", err) 710 } 711 pod := obj.(*api.Pod) 712 713 if !(pod.Annotations != nil && pod.Annotations["label1"] == "value1") { 714 t.Fatalf("Pod annotations don't match the expected: %v", pod.Annotations) 715 } 716 } 717 718 func TestEtcdCreateWithConflict(t *testing.T) { 719 storage, bindingStorage, _, server := newStorage(t) 720 defer server.Terminate(t) 721 defer storage.Store.DestroyFunc() 722 ctx := genericapirequest.NewDefaultContext() 723 724 _, err := storage.Create(ctx, validNewPod(), rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 725 if err != nil { 726 t.Fatalf("unexpected error: %v", err) 727 } 728 729 // Suddenly, a wild scheduler appears: 730 binding := api.Binding{ 731 ObjectMeta: metav1.ObjectMeta{ 732 Namespace: metav1.NamespaceDefault, 733 Name: "foo", 734 Annotations: map[string]string{"label1": "value1"}, 735 }, 736 Target: api.ObjectReference{Name: "machine"}, 737 } 738 _, err = bindingStorage.Create(ctx, binding.Name, &binding, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 739 if err != nil { 740 t.Fatalf("unexpected error: %v", err) 741 } 742 743 _, err = bindingStorage.Create(ctx, binding.Name, &binding, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 744 if err == nil || !errors.IsConflict(err) { 745 t.Fatalf("expected resource conflict error, not: %v", err) 746 } 747 } 748 749 func TestEtcdCreateWithSchedulingGates(t *testing.T) { 750 tests := []struct { 751 name string 752 schedulingGates []api.PodSchedulingGate 753 wantErr error 754 }{ 755 { 756 name: "pod with non-nil schedulingGates", 757 schedulingGates: []api.PodSchedulingGate{ 758 {Name: "foo"}, 759 {Name: "bar"}, 760 }, 761 wantErr: goerrors.New(`Operation cannot be fulfilled on pods/binding "foo": pod foo has non-empty .spec.schedulingGates`), 762 }, 763 { 764 name: "pod with nil schedulingGates", 765 schedulingGates: nil, 766 wantErr: nil, 767 }, 768 } 769 770 for _, tt := range tests { 771 t.Run(tt.name, func(t *testing.T) { 772 storage, bindingStorage, _, server := newStorage(t) 773 defer server.Terminate(t) 774 defer storage.Store.DestroyFunc() 775 ctx := genericapirequest.NewDefaultContext() 776 777 pod := validNewPod() 778 pod.Spec.SchedulingGates = tt.schedulingGates 779 if _, err := storage.Create(ctx, pod, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}); err != nil { 780 t.Fatalf("Unexpected error: %v", err) 781 } 782 _, err := bindingStorage.Create(ctx, "foo", &api.Binding{ 783 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "foo"}, 784 Target: api.ObjectReference{Name: "machine"}, 785 }, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 786 if tt.wantErr == nil { 787 if err != nil { 788 t.Errorf("Want nil err, but got %v", err) 789 } 790 } else { 791 if err == nil { 792 t.Errorf("Want %v, but got nil err", tt.wantErr) 793 } else if tt.wantErr.Error() != err.Error() { 794 t.Errorf("Want %v, but got %v", tt.wantErr, err) 795 } 796 } 797 }) 798 } 799 } 800 801 func validNewBinding() *api.Binding { 802 return &api.Binding{ 803 ObjectMeta: metav1.ObjectMeta{Name: "foo"}, 804 Target: api.ObjectReference{Name: "machine", Kind: "Node"}, 805 } 806 } 807 808 func TestEtcdCreateBindingWithUIDAndResourceVersion(t *testing.T) { 809 originUID := func(pod *api.Pod) types.UID { 810 return pod.UID 811 } 812 emptyUID := func(pod *api.Pod) types.UID { 813 return "" 814 } 815 changedUID := func(pod *api.Pod) types.UID { 816 return pod.UID + "-changed" 817 } 818 819 originResourceVersion := func(pod *api.Pod) string { 820 return pod.ResourceVersion 821 } 822 emptyResourceVersion := func(pod *api.Pod) string { 823 return "" 824 } 825 changedResourceVersion := func(pod *api.Pod) string { 826 return pod.ResourceVersion + "-changed" 827 } 828 829 noError := func(err error) bool { 830 return err == nil 831 } 832 conflictError := func(err error) bool { 833 return err != nil && errors.IsConflict(err) 834 } 835 836 testCases := map[string]struct { 837 podUIDGetter func(pod *api.Pod) types.UID 838 podResourceVersionGetter func(pod *api.Pod) string 839 errOK func(error) bool 840 expectedNodeName string 841 }{ 842 "originUID-originResourceVersion": { 843 podUIDGetter: originUID, 844 podResourceVersionGetter: originResourceVersion, 845 errOK: noError, 846 expectedNodeName: "machine", 847 }, 848 "originUID-emptyResourceVersion": { 849 podUIDGetter: originUID, 850 podResourceVersionGetter: emptyResourceVersion, 851 errOK: noError, 852 expectedNodeName: "machine", 853 }, 854 "originUID-changedResourceVersion": { 855 podUIDGetter: originUID, 856 podResourceVersionGetter: changedResourceVersion, 857 errOK: conflictError, 858 expectedNodeName: "", 859 }, 860 "emptyUID-originResourceVersion": { 861 podUIDGetter: emptyUID, 862 podResourceVersionGetter: originResourceVersion, 863 errOK: noError, 864 expectedNodeName: "machine", 865 }, 866 "emptyUID-emptyResourceVersion": { 867 podUIDGetter: emptyUID, 868 podResourceVersionGetter: emptyResourceVersion, 869 errOK: noError, 870 expectedNodeName: "machine", 871 }, 872 "emptyUID-changedResourceVersion": { 873 podUIDGetter: emptyUID, 874 podResourceVersionGetter: changedResourceVersion, 875 errOK: conflictError, 876 expectedNodeName: "", 877 }, 878 "changedUID-originResourceVersion": { 879 podUIDGetter: changedUID, 880 podResourceVersionGetter: originResourceVersion, 881 errOK: conflictError, 882 expectedNodeName: "", 883 }, 884 "changedUID-emptyResourceVersion": { 885 podUIDGetter: changedUID, 886 podResourceVersionGetter: emptyResourceVersion, 887 errOK: conflictError, 888 expectedNodeName: "", 889 }, 890 "changedUID-changedResourceVersion": { 891 podUIDGetter: changedUID, 892 podResourceVersionGetter: changedResourceVersion, 893 errOK: conflictError, 894 expectedNodeName: "", 895 }, 896 } 897 898 storage, bindingStorage, _, server := newStorage(t) 899 defer server.Terminate(t) 900 defer storage.Store.DestroyFunc() 901 902 for k, testCase := range testCases { 903 pod := validNewPod() 904 pod.Namespace = fmt.Sprintf("namespace-%s", strings.ToLower(k)) 905 ctx := genericapirequest.WithNamespace(genericapirequest.NewDefaultContext(), pod.Namespace) 906 907 podCreated, err := storage.Create(ctx, pod, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 908 if err != nil { 909 t.Fatalf("%s: unexpected error: %v", k, err) 910 } 911 912 binding := validNewBinding() 913 binding.UID = testCase.podUIDGetter(podCreated.(*api.Pod)) 914 binding.ResourceVersion = testCase.podResourceVersionGetter(podCreated.(*api.Pod)) 915 916 if _, err := bindingStorage.Create(ctx, binding.Name, binding, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}); !testCase.errOK(err) { 917 t.Errorf("%s: unexpected error: %v", k, err) 918 } 919 920 if pod, err := storage.Get(ctx, pod.Name, &metav1.GetOptions{}); err != nil { 921 t.Errorf("%s: unexpected error: %v", k, err) 922 } else if pod.(*api.Pod).Spec.NodeName != testCase.expectedNodeName { 923 t.Errorf("%s: expected: %v, got: %v", k, pod.(*api.Pod).Spec.NodeName, testCase.expectedNodeName) 924 } 925 } 926 } 927 928 func TestEtcdCreateWithExistingContainers(t *testing.T) { 929 storage, bindingStorage, _, server := newStorage(t) 930 defer server.Terminate(t) 931 defer storage.Store.DestroyFunc() 932 ctx := genericapirequest.NewDefaultContext() 933 _, err := storage.Create(ctx, validNewPod(), rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 934 if err != nil { 935 t.Fatalf("unexpected error: %v", err) 936 } 937 938 // Suddenly, a wild scheduler appears: 939 _, err = bindingStorage.Create(ctx, "foo", &api.Binding{ 940 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "foo"}, 941 Target: api.ObjectReference{Name: "machine"}, 942 }, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}) 943 if err != nil { 944 t.Fatalf("unexpected error: %v", err) 945 } 946 947 _, err = storage.Get(ctx, "foo", &metav1.GetOptions{}) 948 if err != nil { 949 t.Fatalf("Unexpected error %v", err) 950 } 951 } 952 953 func TestEtcdCreateBinding(t *testing.T) { 954 testCases := map[string]struct { 955 binding api.Binding 956 badNameInURL bool 957 errOK func(error) bool 958 }{ 959 "noName": { 960 binding: api.Binding{ 961 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "foo"}, 962 Target: api.ObjectReference{}, 963 }, 964 errOK: func(err error) bool { return err != nil }, 965 }, 966 "badNameInURL": { 967 binding: api.Binding{ 968 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "foo"}, 969 Target: api.ObjectReference{}, 970 }, 971 badNameInURL: true, 972 errOK: func(err error) bool { return err != nil }, 973 }, 974 "badKind": { 975 binding: api.Binding{ 976 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "foo"}, 977 Target: api.ObjectReference{Name: "machine1", Kind: "unknown"}, 978 }, 979 errOK: func(err error) bool { return err != nil }, 980 }, 981 "emptyKind": { 982 binding: api.Binding{ 983 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "foo"}, 984 Target: api.ObjectReference{Name: "machine2"}, 985 }, 986 errOK: func(err error) bool { return err == nil }, 987 }, 988 "kindNode": { 989 binding: api.Binding{ 990 ObjectMeta: metav1.ObjectMeta{Namespace: metav1.NamespaceDefault, Name: "foo"}, 991 Target: api.ObjectReference{Name: "machine3", Kind: "Node"}, 992 }, 993 errOK: func(err error) bool { return err == nil }, 994 }, 995 } 996 storage, bindingStorage, _, server := newStorage(t) 997 defer server.Terminate(t) 998 defer storage.Store.DestroyFunc() 999 1000 for k, test := range testCases { 1001 pod := validNewPod() 1002 pod.Namespace = fmt.Sprintf("namespace-%s", strings.ToLower(k)) 1003 ctx := genericapirequest.WithNamespace(genericapirequest.NewDefaultContext(), pod.Namespace) 1004 if _, err := storage.Create(ctx, pod, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}); err != nil { 1005 t.Fatalf("%s: unexpected error: %v", k, err) 1006 } 1007 name := test.binding.Name 1008 if test.badNameInURL { 1009 name += "badNameInURL" 1010 } 1011 if _, err := bindingStorage.Create(ctx, name, &test.binding, rest.ValidateAllObjectFunc, &metav1.CreateOptions{}); !test.errOK(err) { 1012 t.Errorf("%s: unexpected error: %v", k, err) 1013 } else if err == nil { 1014 // If bind succeeded, verify Host field in pod's Spec. 1015 pod, err := storage.Get(ctx, pod.ObjectMeta.Name, &metav1.GetOptions{}) 1016 if err != nil { 1017 t.Errorf("%s: unexpected error: %v", k, err) 1018 } else if pod.(*api.Pod).Spec.NodeName != test.binding.Target.Name { 1019 t.Errorf("%s: expected: %v, got: %v", k, pod.(*api.Pod).Spec.NodeName, test.binding.Target.Name) 1020 } 1021 } 1022 } 1023 } 1024 1025 func TestEtcdUpdateNotScheduled(t *testing.T) { 1026 storage, _, _, server := newStorage(t) 1027 defer server.Terminate(t) 1028 defer storage.Store.DestroyFunc() 1029 ctx := genericapirequest.NewDefaultContext() 1030 1031 if _, err := storage.Create(ctx, validNewPod(), rest.ValidateAllObjectFunc, &metav1.CreateOptions{}); err != nil { 1032 t.Fatalf("unexpected error: %v", err) 1033 } 1034 1035 podIn := validChangedPod() 1036 _, _, err := storage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(podIn), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}) 1037 if err != nil { 1038 t.Errorf("Unexpected error: %v", err) 1039 } 1040 obj, err := storage.Get(ctx, validNewPod().ObjectMeta.Name, &metav1.GetOptions{}) 1041 if err != nil { 1042 t.Errorf("unexpected error: %v", err) 1043 } 1044 podOut := obj.(*api.Pod) 1045 // validChangedPod only changes the Labels, so were checking the update was valid 1046 if !apiequality.Semantic.DeepEqual(podIn.Labels, podOut.Labels) { 1047 t.Errorf("objects differ: %v", cmp.Diff(podOut, podIn)) 1048 } 1049 } 1050 1051 func TestEtcdUpdateScheduled(t *testing.T) { 1052 storage, _, _, server := newStorage(t) 1053 defer server.Terminate(t) 1054 defer storage.Store.DestroyFunc() 1055 ctx := genericapirequest.NewDefaultContext() 1056 1057 key, _ := storage.KeyFunc(ctx, "foo") 1058 err := storage.Storage.Create(ctx, key, &api.Pod{ 1059 ObjectMeta: metav1.ObjectMeta{ 1060 Name: "foo", 1061 Namespace: metav1.NamespaceDefault, 1062 }, 1063 Spec: api.PodSpec{ 1064 NodeName: "machine", 1065 Containers: []api.Container{ 1066 { 1067 Name: "foobar", 1068 Image: "foo:v1", 1069 SecurityContext: securitycontext.ValidInternalSecurityContextWithContainerDefaults(), 1070 }, 1071 }, 1072 SecurityContext: &api.PodSecurityContext{}, 1073 SchedulerName: v1.DefaultSchedulerName, 1074 }, 1075 }, nil, 1, false) 1076 if err != nil { 1077 t.Errorf("Unexpected error: %v", err) 1078 } 1079 1080 grace := int64(30) 1081 enableServiceLinks := v1.DefaultEnableServiceLinks 1082 podIn := api.Pod{ 1083 ObjectMeta: metav1.ObjectMeta{ 1084 Name: "foo", 1085 Labels: map[string]string{ 1086 "foo": "bar", 1087 }, 1088 }, 1089 Spec: api.PodSpec{ 1090 NodeName: "machine", 1091 Containers: []api.Container{{ 1092 Name: "foobar", 1093 Image: "foo:v2", 1094 ImagePullPolicy: api.PullIfNotPresent, 1095 TerminationMessagePath: api.TerminationMessagePathDefault, 1096 TerminationMessagePolicy: api.TerminationMessageReadFile, 1097 SecurityContext: securitycontext.ValidInternalSecurityContextWithContainerDefaults(), 1098 }}, 1099 RestartPolicy: api.RestartPolicyAlways, 1100 DNSPolicy: api.DNSClusterFirst, 1101 1102 TerminationGracePeriodSeconds: &grace, 1103 SecurityContext: &api.PodSecurityContext{}, 1104 SchedulerName: v1.DefaultSchedulerName, 1105 EnableServiceLinks: &enableServiceLinks, 1106 }, 1107 } 1108 _, _, err = storage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(&podIn), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}) 1109 if err != nil { 1110 t.Errorf("Unexpected error: %v", err) 1111 } 1112 1113 obj, err := storage.Get(ctx, "foo", &metav1.GetOptions{}) 1114 if err != nil { 1115 t.Errorf("Unexpected error: %v", err) 1116 } 1117 podOut := obj.(*api.Pod) 1118 // Check to verify the Spec and Label updates match from change above. Those are the fields changed. 1119 if !apiequality.Semantic.DeepEqual(podOut.Spec, podIn.Spec) || !apiequality.Semantic.DeepEqual(podOut.Labels, podIn.Labels) { 1120 t.Errorf("objects differ: %v", cmp.Diff(podOut, podIn)) 1121 } 1122 1123 } 1124 1125 func TestEtcdUpdateStatus(t *testing.T) { 1126 storage, _, statusStorage, server := newStorage(t) 1127 defer server.Terminate(t) 1128 defer storage.Store.DestroyFunc() 1129 ctx := genericapirequest.NewDefaultContext() 1130 1131 key, _ := storage.KeyFunc(ctx, "foo") 1132 podStart := api.Pod{ 1133 ObjectMeta: metav1.ObjectMeta{ 1134 Name: "foo", 1135 Namespace: metav1.NamespaceDefault, 1136 }, 1137 Spec: api.PodSpec{ 1138 NodeName: "machine", 1139 Containers: []api.Container{ 1140 { 1141 Image: "foo:v1", 1142 SecurityContext: securitycontext.ValidInternalSecurityContextWithContainerDefaults(), 1143 }, 1144 }, 1145 SecurityContext: &api.PodSecurityContext{}, 1146 SchedulerName: v1.DefaultSchedulerName, 1147 }, 1148 } 1149 err := storage.Storage.Create(ctx, key, &podStart, nil, 0, false) 1150 if err != nil { 1151 t.Errorf("unexpected error: %v", err) 1152 } 1153 1154 podsIn := []api.Pod{ 1155 { 1156 ObjectMeta: metav1.ObjectMeta{ 1157 Name: "foo", 1158 Labels: map[string]string{ 1159 "foo": "bar", 1160 }, 1161 }, 1162 Spec: api.PodSpec{ 1163 NodeName: "machine", 1164 Containers: []api.Container{ 1165 { 1166 Image: "foo:v2", 1167 ImagePullPolicy: api.PullIfNotPresent, 1168 TerminationMessagePath: api.TerminationMessagePathDefault, 1169 }, 1170 }, 1171 SecurityContext: &api.PodSecurityContext{}, 1172 SchedulerName: v1.DefaultSchedulerName, 1173 }, 1174 Status: api.PodStatus{ 1175 Phase: api.PodRunning, 1176 PodIPs: []api.PodIP{{IP: "127.0.0.1"}}, 1177 Message: "is now scheduled", 1178 }, 1179 }, 1180 { 1181 ObjectMeta: metav1.ObjectMeta{ 1182 Name: "foo", 1183 Labels: map[string]string{ 1184 "foo": "bar", 1185 }, 1186 }, 1187 Spec: api.PodSpec{ 1188 NodeName: "machine", 1189 Containers: []api.Container{ 1190 { 1191 Image: "foo:v2", 1192 ImagePullPolicy: api.PullIfNotPresent, 1193 TerminationMessagePath: api.TerminationMessagePathDefault, 1194 }, 1195 }, 1196 SecurityContext: &api.PodSecurityContext{}, 1197 SchedulerName: v1.DefaultSchedulerName, 1198 }, 1199 Status: api.PodStatus{ 1200 Phase: api.PodRunning, 1201 PodIPs: []api.PodIP{{IP: "127.0.0.1"}, {IP: "2001:db8::"}}, 1202 Message: "is now scheduled", 1203 }, 1204 }, 1205 } 1206 1207 for _, podIn := range podsIn { 1208 expected := podStart 1209 expected.ResourceVersion = "2" 1210 grace := int64(30) 1211 enableServiceLinks := v1.DefaultEnableServiceLinks 1212 expected.Spec.TerminationGracePeriodSeconds = &grace 1213 expected.Spec.RestartPolicy = api.RestartPolicyAlways 1214 expected.Spec.DNSPolicy = api.DNSClusterFirst 1215 expected.Spec.EnableServiceLinks = &enableServiceLinks 1216 expected.Spec.Containers[0].ImagePullPolicy = api.PullIfNotPresent 1217 expected.Spec.Containers[0].TerminationMessagePath = api.TerminationMessagePathDefault 1218 expected.Spec.Containers[0].TerminationMessagePolicy = api.TerminationMessageReadFile 1219 expected.Labels = podIn.Labels 1220 expected.Status = podIn.Status 1221 1222 _, _, err = statusStorage.Update(ctx, podIn.Name, rest.DefaultUpdatedObjectInfo(&podIn), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{}) 1223 if err != nil { 1224 t.Fatalf("Unexpected error: %v", err) 1225 } 1226 obj, err := storage.Get(ctx, "foo", &metav1.GetOptions{}) 1227 if err != nil { 1228 t.Errorf("unexpected error: %v", err) 1229 } 1230 podOut := obj.(*api.Pod) 1231 // Check to verify the Label, and Status updates match from change above. Those are the fields changed. 1232 if !apiequality.Semantic.DeepEqual(podOut.Spec, expected.Spec) || 1233 !apiequality.Semantic.DeepEqual(podOut.Labels, expected.Labels) || 1234 !apiequality.Semantic.DeepEqual(podOut.Status, expected.Status) { 1235 t.Errorf("objects differ: %v", cmp.Diff(podOut, expected)) 1236 } 1237 } 1238 } 1239 1240 func TestShortNames(t *testing.T) { 1241 storage, _, _, server := newStorage(t) 1242 defer server.Terminate(t) 1243 defer storage.Store.DestroyFunc() 1244 expected := []string{"po"} 1245 registrytest.AssertShortNames(t, storage, expected) 1246 } 1247 1248 func TestCategories(t *testing.T) { 1249 storage, _, _, server := newStorage(t) 1250 defer server.Terminate(t) 1251 defer storage.Store.DestroyFunc() 1252 expected := []string{"all"} 1253 registrytest.AssertCategories(t, storage, expected) 1254 }