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  }