...

Source file src/k8s.io/kubernetes/pkg/registry/batch/job/storage/storage_test.go

Documentation: k8s.io/kubernetes/pkg/registry/batch/job/storage

     1  /*
     2  Copyright 2015 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  	"k8s.io/utils/pointer"
    21  	"testing"
    22  
    23  	batchv1 "k8s.io/api/batch/v1"
    24  	corev1 "k8s.io/api/core/v1"
    25  	"k8s.io/apimachinery/pkg/apis/meta/internalversion"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/fields"
    28  	"k8s.io/apimachinery/pkg/labels"
    29  	"k8s.io/apimachinery/pkg/runtime"
    30  	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
    31  	"k8s.io/apiserver/pkg/registry/generic"
    32  	genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
    33  	"k8s.io/apiserver/pkg/registry/rest"
    34  	etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing"
    35  	"k8s.io/apiserver/pkg/warning"
    36  	"k8s.io/kubernetes/pkg/apis/batch"
    37  	api "k8s.io/kubernetes/pkg/apis/core"
    38  	"k8s.io/kubernetes/pkg/registry/registrytest"
    39  )
    40  
    41  func newStorage(t *testing.T) (*JobStorage, *etcd3testing.EtcdTestServer) {
    42  	etcdStorage, server := registrytest.NewEtcdStorage(t, batch.GroupName)
    43  	restOptions := generic.RESTOptions{
    44  		StorageConfig:           etcdStorage,
    45  		Decorator:               generic.UndecoratedStorage,
    46  		DeleteCollectionWorkers: 1,
    47  		ResourcePrefix:          "jobs",
    48  	}
    49  	jobStorage, err := NewStorage(restOptions)
    50  	if err != nil {
    51  		t.Fatalf("unexpected error from REST storage: %v", err)
    52  	}
    53  	return &jobStorage, server
    54  }
    55  
    56  func validNewJob() *batch.Job {
    57  	completions := int32(1)
    58  	parallelism := int32(1)
    59  	return &batch.Job{
    60  		ObjectMeta: metav1.ObjectMeta{
    61  			Name:      "foo",
    62  			Namespace: "default",
    63  		},
    64  		Spec: batch.JobSpec{
    65  			Completions: &completions,
    66  			Parallelism: &parallelism,
    67  			Selector: &metav1.LabelSelector{
    68  				MatchLabels: map[string]string{"a": "b"},
    69  			},
    70  			ManualSelector: newBool(true),
    71  			Template: api.PodTemplateSpec{
    72  				ObjectMeta: metav1.ObjectMeta{
    73  					Labels: map[string]string{"a": "b"},
    74  				},
    75  				Spec: api.PodSpec{
    76  					Containers: []api.Container{
    77  						{
    78  							Name:                     "test",
    79  							Image:                    "test_image",
    80  							ImagePullPolicy:          api.PullIfNotPresent,
    81  							TerminationMessagePolicy: api.TerminationMessageReadFile,
    82  						},
    83  					},
    84  					RestartPolicy: api.RestartPolicyOnFailure,
    85  					DNSPolicy:     api.DNSClusterFirst,
    86  				},
    87  			},
    88  		},
    89  	}
    90  }
    91  
    92  func validNewV1Job() *batchv1.Job {
    93  	completions := int32(1)
    94  	parallelism := int32(1)
    95  	return &batchv1.Job{
    96  		ObjectMeta: metav1.ObjectMeta{
    97  			Name:      "foo",
    98  			Namespace: "default",
    99  		},
   100  		Spec: batchv1.JobSpec{
   101  			Completions: &completions,
   102  			Parallelism: &parallelism,
   103  			Selector: &metav1.LabelSelector{
   104  				MatchLabels: map[string]string{"a": "b"},
   105  			},
   106  			ManualSelector: newBool(true),
   107  			Template: corev1.PodTemplateSpec{
   108  				ObjectMeta: metav1.ObjectMeta{
   109  					Labels: map[string]string{"a": "b"},
   110  				},
   111  				Spec: corev1.PodSpec{
   112  					Containers: []corev1.Container{
   113  						{
   114  							Name:                     "test",
   115  							Image:                    "test_image",
   116  							ImagePullPolicy:          corev1.PullIfNotPresent,
   117  							TerminationMessagePolicy: corev1.TerminationMessageReadFile,
   118  						},
   119  					},
   120  					RestartPolicy: corev1.RestartPolicyOnFailure,
   121  					DNSPolicy:     corev1.DNSClusterFirst,
   122  				},
   123  			},
   124  		},
   125  	}
   126  }
   127  
   128  func TestCreate(t *testing.T) {
   129  	storage, server := newStorage(t)
   130  	defer server.Terminate(t)
   131  	defer storage.Job.Store.DestroyFunc()
   132  	test := genericregistrytest.New(t, storage.Job.Store)
   133  	validJob := validNewJob()
   134  	validJob.ObjectMeta = metav1.ObjectMeta{}
   135  	test.TestCreate(
   136  		// valid
   137  		validJob,
   138  		// invalid (empty selector)
   139  		&batch.Job{
   140  			Spec: batch.JobSpec{
   141  				ManualSelector: pointer.Bool(false),
   142  				Completions:    validJob.Spec.Completions,
   143  				Selector:       &metav1.LabelSelector{},
   144  				Template:       validJob.Spec.Template,
   145  			},
   146  		},
   147  	)
   148  }
   149  
   150  func TestUpdate(t *testing.T) {
   151  	storage, server := newStorage(t)
   152  	defer server.Terminate(t)
   153  	defer storage.Job.Store.DestroyFunc()
   154  	test := genericregistrytest.New(t, storage.Job.Store)
   155  	two := int32(2)
   156  	test.TestUpdate(
   157  		// valid
   158  		validNewJob(),
   159  		// updateFunc
   160  		func(obj runtime.Object) runtime.Object {
   161  			object := obj.(*batch.Job)
   162  			object.Spec.Parallelism = &two
   163  			return object
   164  		},
   165  		// invalid updateFunc
   166  		func(obj runtime.Object) runtime.Object {
   167  			object := obj.(*batch.Job)
   168  			object.Spec.Selector = &metav1.LabelSelector{}
   169  			return object
   170  		},
   171  		func(obj runtime.Object) runtime.Object {
   172  			object := obj.(*batch.Job)
   173  			object.Spec.Completions = &two
   174  			return object
   175  		},
   176  	)
   177  }
   178  
   179  func TestDelete(t *testing.T) {
   180  	storage, server := newStorage(t)
   181  	defer server.Terminate(t)
   182  	defer storage.Job.Store.DestroyFunc()
   183  	test := genericregistrytest.New(t, storage.Job.Store)
   184  	test.TestDelete(validNewJob())
   185  }
   186  
   187  type dummyRecorder struct {
   188  	agent string
   189  	text  string
   190  }
   191  
   192  func (r *dummyRecorder) AddWarning(agent, text string) {
   193  	r.agent = agent
   194  	r.text = text
   195  	return
   196  }
   197  
   198  func (r *dummyRecorder) getWarning() string {
   199  	return r.text
   200  }
   201  
   202  var _ warning.Recorder = &dummyRecorder{}
   203  
   204  func TestJobDeletion(t *testing.T) {
   205  	orphanDependents := true
   206  	orphanDeletionPropagation := metav1.DeletePropagationOrphan
   207  	backgroundDeletionPropagation := metav1.DeletePropagationBackground
   208  	job := validNewV1Job()
   209  	ctx := genericapirequest.NewDefaultContext()
   210  	key := "/jobs/" + metav1.NamespaceDefault + "/foo"
   211  	tests := []struct {
   212  		description   string
   213  		expectWarning bool
   214  		deleteOptions *metav1.DeleteOptions
   215  		listOptions   *internalversion.ListOptions
   216  		requestInfo   *genericapirequest.RequestInfo
   217  	}{
   218  		{
   219  			description:   "deletion: no policy, v1, warning",
   220  			expectWarning: true,
   221  			deleteOptions: &metav1.DeleteOptions{},
   222  			requestInfo:   &genericapirequest.RequestInfo{APIGroup: "batch", APIVersion: "v1"},
   223  		},
   224  		{
   225  			description:   "deletion: no policy, v2, no warning",
   226  			expectWarning: false,
   227  			deleteOptions: &metav1.DeleteOptions{},
   228  			requestInfo:   &genericapirequest.RequestInfo{APIGroup: "batch", APIVersion: "v2"},
   229  		},
   230  		{
   231  			description:   "deletion: no policy, no APIVersion, no warning",
   232  			expectWarning: false,
   233  			deleteOptions: &metav1.DeleteOptions{},
   234  			requestInfo:   &genericapirequest.RequestInfo{APIGroup: "batch", APIVersion: ""},
   235  		},
   236  		{
   237  			description:   "deletion: orphan dependents, no warnings",
   238  			expectWarning: false,
   239  			deleteOptions: &metav1.DeleteOptions{OrphanDependents: &orphanDependents},
   240  			requestInfo:   &genericapirequest.RequestInfo{APIGroup: "batch", APIVersion: "v1"},
   241  		},
   242  		{
   243  			description:   "deletion: orphan deletion, no warnings",
   244  			expectWarning: false,
   245  			deleteOptions: &metav1.DeleteOptions{PropagationPolicy: &orphanDeletionPropagation},
   246  			requestInfo:   &genericapirequest.RequestInfo{APIGroup: "batch", APIVersion: "v1"},
   247  		},
   248  		{
   249  			description:   "deletion: background deletion, no warnings",
   250  			expectWarning: false,
   251  			deleteOptions: &metav1.DeleteOptions{PropagationPolicy: &backgroundDeletionPropagation},
   252  			requestInfo:   &genericapirequest.RequestInfo{APIGroup: "batch", APIVersion: "v1"},
   253  		},
   254  		{
   255  			description:   "deleteCollection: no policy, v1, warning",
   256  			expectWarning: true,
   257  			deleteOptions: &metav1.DeleteOptions{},
   258  			listOptions:   &internalversion.ListOptions{},
   259  			requestInfo:   &genericapirequest.RequestInfo{APIGroup: "batch", APIVersion: "v1"},
   260  		},
   261  		{
   262  			description:   "deleteCollection: no policy, v2, no warning",
   263  			expectWarning: false,
   264  			deleteOptions: &metav1.DeleteOptions{},
   265  			listOptions:   &internalversion.ListOptions{},
   266  			requestInfo:   &genericapirequest.RequestInfo{APIGroup: "batch", APIVersion: "v2"},
   267  		},
   268  		{
   269  			description:   "deleteCollection: no policy, no APIVersion, no warning",
   270  			expectWarning: false,
   271  			deleteOptions: &metav1.DeleteOptions{},
   272  			listOptions:   &internalversion.ListOptions{},
   273  			requestInfo:   &genericapirequest.RequestInfo{APIGroup: "batch", APIVersion: ""},
   274  		},
   275  		{
   276  			description:   "deleteCollection: orphan dependents, no warnings",
   277  			expectWarning: false,
   278  			deleteOptions: &metav1.DeleteOptions{OrphanDependents: &orphanDependents},
   279  			listOptions:   &internalversion.ListOptions{},
   280  			requestInfo:   &genericapirequest.RequestInfo{APIGroup: "batch", APIVersion: "v1"},
   281  		},
   282  		{
   283  			description:   "deletionCollection: orphan deletion, no warnings",
   284  			expectWarning: false,
   285  			deleteOptions: &metav1.DeleteOptions{PropagationPolicy: &orphanDeletionPropagation},
   286  			listOptions:   &internalversion.ListOptions{},
   287  			requestInfo:   &genericapirequest.RequestInfo{APIGroup: "batch", APIVersion: "v1"},
   288  		},
   289  		{
   290  			description:   "deletionCollection: background deletion, no warnings",
   291  			expectWarning: false,
   292  			deleteOptions: &metav1.DeleteOptions{PropagationPolicy: &backgroundDeletionPropagation},
   293  			listOptions:   &internalversion.ListOptions{},
   294  			requestInfo:   &genericapirequest.RequestInfo{APIGroup: "batch", APIVersion: "v1"},
   295  		},
   296  	}
   297  	for _, test := range tests {
   298  		t.Run(test.description, func(t *testing.T) {
   299  			storage, server := newStorage(t)
   300  			defer server.Terminate(t)
   301  			defer storage.Job.Store.DestroyFunc()
   302  			dc := dummyRecorder{agent: "", text: ""}
   303  			ctx = genericapirequest.WithRequestInfo(ctx, test.requestInfo)
   304  			ctxWithRecorder := warning.WithWarningRecorder(ctx, &dc)
   305  			// Create the object
   306  			if err := storage.Job.Storage.Create(ctxWithRecorder, key, job, nil, 0, false); err != nil {
   307  				t.Fatalf("unexpected error: %v", err)
   308  			}
   309  			_, _, err := storage.Job.Delete(ctxWithRecorder, job.Name, rest.ValidateAllObjectFunc, test.deleteOptions)
   310  			if err != nil {
   311  				t.Fatalf("unexpected error: %v", err)
   312  			}
   313  			_, err = storage.Job.DeleteCollection(ctxWithRecorder, rest.ValidateAllObjectFunc, test.deleteOptions, test.listOptions)
   314  			if err != nil {
   315  				t.Fatalf("unexpected error: %v", err)
   316  			}
   317  			if test.expectWarning {
   318  				if dc.getWarning() != deleteOptionWarnings {
   319  					t.Fatalf("expected delete option warning but did not get one")
   320  				}
   321  			}
   322  		})
   323  	}
   324  }
   325  
   326  func TestGet(t *testing.T) {
   327  	storage, server := newStorage(t)
   328  	defer server.Terminate(t)
   329  	defer storage.Job.Store.DestroyFunc()
   330  	test := genericregistrytest.New(t, storage.Job.Store)
   331  	test.TestGet(validNewJob())
   332  }
   333  
   334  func TestList(t *testing.T) {
   335  	storage, server := newStorage(t)
   336  	defer server.Terminate(t)
   337  	defer storage.Job.Store.DestroyFunc()
   338  	test := genericregistrytest.New(t, storage.Job.Store)
   339  	test.TestList(validNewJob())
   340  }
   341  
   342  func TestWatch(t *testing.T) {
   343  	storage, server := newStorage(t)
   344  	defer server.Terminate(t)
   345  	defer storage.Job.Store.DestroyFunc()
   346  	test := genericregistrytest.New(t, storage.Job.Store)
   347  	test.TestWatch(
   348  		validNewJob(),
   349  		// matching labels
   350  		[]labels.Set{},
   351  		// not matching labels
   352  		[]labels.Set{
   353  			{"x": "y"},
   354  		},
   355  		// matching fields
   356  		[]fields.Set{},
   357  		// not matching fields
   358  		[]fields.Set{
   359  			{"metadata.name": "xyz"},
   360  			{"name": "foo"},
   361  		},
   362  	)
   363  }
   364  
   365  // TODO: test update /status
   366  
   367  func newBool(val bool) *bool {
   368  	p := new(bool)
   369  	*p = val
   370  	return p
   371  }
   372  
   373  func TestCategories(t *testing.T) {
   374  	storage, server := newStorage(t)
   375  	defer server.Terminate(t)
   376  	defer storage.Job.Store.DestroyFunc()
   377  	expected := []string{"all"}
   378  	registrytest.AssertCategories(t, storage.Job, expected)
   379  }
   380  

View as plain text