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