mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-26 21:17:23 +00:00
Merge pull request #99791 from soltysh/simplify_unittests
Simplify cronjob v2 controller tests
This commit is contained in:
commit
bf67ba1c0e
@ -17,7 +17,6 @@ limitations under the License.
|
|||||||
package cronjob
|
package cronjob
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@ -29,13 +28,15 @@ import (
|
|||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
batchv1listers "k8s.io/client-go/listers/batch/v1"
|
"k8s.io/client-go/informers"
|
||||||
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
"k8s.io/client-go/util/workqueue"
|
"k8s.io/client-go/util/workqueue"
|
||||||
_ "k8s.io/kubernetes/pkg/apis/batch/install"
|
_ "k8s.io/kubernetes/pkg/apis/batch/install"
|
||||||
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
||||||
|
"k8s.io/kubernetes/pkg/controller"
|
||||||
)
|
)
|
||||||
|
|
||||||
func justASecondBeforeTheHour() time.Time {
|
func justASecondBeforeTheHour() time.Time {
|
||||||
@ -46,7 +47,7 @@ func justASecondBeforeTheHour() time.Time {
|
|||||||
return T1
|
return T1
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_syncCronJob(t *testing.T) {
|
func TestControllerV2SyncCronJob(t *testing.T) {
|
||||||
// Check expectations on deadline parameters
|
// Check expectations on deadline parameters
|
||||||
if shortDead/60/60 >= 1 {
|
if shortDead/60/60 >= 1 {
|
||||||
t.Errorf("shortDead should be less than one hour")
|
t.Errorf("shortDead should be less than one hour")
|
||||||
@ -312,240 +313,166 @@ func Test_syncCronJob(t *testing.T) {
|
|||||||
|
|
||||||
type fakeQueue struct {
|
type fakeQueue struct {
|
||||||
workqueue.RateLimitingInterface
|
workqueue.RateLimitingInterface
|
||||||
t time.Duration
|
delay time.Duration
|
||||||
key interface{}
|
key interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeQueue) AddAfter(key interface{}, t time.Duration) {
|
func (f *fakeQueue) AddAfter(key interface{}, delay time.Duration) {
|
||||||
f.t = t
|
f.delay = delay
|
||||||
f.key = key
|
f.key = key
|
||||||
}
|
}
|
||||||
|
|
||||||
// this test will take around 1 seconds to complete
|
// this test will take around 61 seconds to complete
|
||||||
func TestController2_updateCronJob(t *testing.T) {
|
func TestControllerV2UpdateCronJob(t *testing.T) {
|
||||||
cjc := &fakeCJControl{}
|
|
||||||
jc := &fakeJobControl{}
|
|
||||||
type fields struct {
|
|
||||||
queue *fakeQueue
|
|
||||||
recorder record.EventRecorder
|
|
||||||
jobControl jobControlInterface
|
|
||||||
cronJobControl cjControlInterface
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
oldJobTemplate *batchv1.JobTemplateSpec
|
|
||||||
newJobTemplate *batchv1.JobTemplateSpec
|
|
||||||
oldJobSchedule string
|
|
||||||
newJobSchedule string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
fields fields
|
oldCronJob *batchv1.CronJob
|
||||||
args args
|
newCronJob *batchv1.CronJob
|
||||||
deltaTimeForQueue time.Duration
|
expectedDelay time.Duration
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "spec.template changed",
|
name: "spec.template changed",
|
||||||
fields: fields{
|
oldCronJob: &batchv1.CronJob{
|
||||||
queue: &fakeQueue{RateLimitingInterface: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "test-update-cronjob")},
|
Spec: batchv1.CronJobSpec{
|
||||||
recorder: record.NewFakeRecorder(10),
|
JobTemplate: batchv1.JobTemplateSpec{
|
||||||
jobControl: jc,
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
cronJobControl: cjc,
|
Labels: map[string]string{"a": "b"},
|
||||||
},
|
Annotations: map[string]string{"x": "y"},
|
||||||
args: args{
|
},
|
||||||
oldJobTemplate: &batchv1.JobTemplateSpec{
|
Spec: jobSpec(),
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Labels: map[string]string{"a": "b"},
|
|
||||||
Annotations: map[string]string{"x": "y"},
|
|
||||||
},
|
},
|
||||||
Spec: jobSpec(),
|
|
||||||
},
|
|
||||||
newJobTemplate: &batchv1.JobTemplateSpec{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Labels: map[string]string{"a": "foo"},
|
|
||||||
Annotations: map[string]string{"x": "y"},
|
|
||||||
},
|
|
||||||
Spec: jobSpec(),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
deltaTimeForQueue: 0 * time.Second,
|
newCronJob: &batchv1.CronJob{
|
||||||
|
Spec: batchv1.CronJobSpec{
|
||||||
|
JobTemplate: batchv1.JobTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: map[string]string{"a": "foo"},
|
||||||
|
Annotations: map[string]string{"x": "y"},
|
||||||
|
},
|
||||||
|
Spec: jobSpec(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedDelay: 0 * time.Second,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "spec.schedule changed",
|
name: "spec.schedule changed",
|
||||||
fields: fields{
|
oldCronJob: &batchv1.CronJob{
|
||||||
queue: &fakeQueue{RateLimitingInterface: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "test-update-cronjob")},
|
Spec: batchv1.CronJobSpec{
|
||||||
recorder: record.NewFakeRecorder(10),
|
Schedule: "30 * * * *",
|
||||||
jobControl: jc,
|
JobTemplate: batchv1.JobTemplateSpec{
|
||||||
cronJobControl: cjc,
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: map[string]string{"a": "b"},
|
||||||
|
Annotations: map[string]string{"x": "y"},
|
||||||
|
},
|
||||||
|
Spec: jobSpec(),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
args: args{
|
newCronJob: &batchv1.CronJob{
|
||||||
oldJobSchedule: "30 * * * *",
|
Spec: batchv1.CronJobSpec{
|
||||||
newJobSchedule: "*/1 * * * *",
|
Schedule: "*/1 * * * *",
|
||||||
|
JobTemplate: batchv1.JobTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: map[string]string{"a": "foo"},
|
||||||
|
Annotations: map[string]string{"x": "y"},
|
||||||
|
},
|
||||||
|
Spec: jobSpec(),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
deltaTimeForQueue: 1*time.Second + nextScheduleDelta,
|
expectedDelay: 1*time.Second + nextScheduleDelta,
|
||||||
},
|
},
|
||||||
// TODO: Add more test cases for updating scheduling.
|
// TODO: Add more test cases for updating scheduling.
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
cj := cronJob()
|
kubeClient := fake.NewSimpleClientset()
|
||||||
newCj := cronJob()
|
sharedInformers := informers.NewSharedInformerFactory(kubeClient, controller.NoResyncPeriodFunc())
|
||||||
if tt.args.oldJobTemplate != nil {
|
jm, err := NewControllerV2(sharedInformers.Batch().V1().Jobs(), sharedInformers.Batch().V1().CronJobs(), kubeClient)
|
||||||
cj.Spec.JobTemplate = *tt.args.oldJobTemplate
|
if err != nil {
|
||||||
}
|
t.Errorf("unexpected error %v", err)
|
||||||
if tt.args.newJobTemplate != nil {
|
return
|
||||||
newCj.Spec.JobTemplate = *tt.args.newJobTemplate
|
|
||||||
}
|
|
||||||
if tt.args.oldJobSchedule != "" {
|
|
||||||
cj.Spec.Schedule = tt.args.oldJobSchedule
|
|
||||||
}
|
|
||||||
if tt.args.newJobSchedule != "" {
|
|
||||||
newCj.Spec.Schedule = tt.args.newJobSchedule
|
|
||||||
}
|
|
||||||
jm := &ControllerV2{
|
|
||||||
queue: tt.fields.queue,
|
|
||||||
recorder: tt.fields.recorder,
|
|
||||||
jobControl: tt.fields.jobControl,
|
|
||||||
cronJobControl: tt.fields.cronJobControl,
|
|
||||||
}
|
}
|
||||||
jm.now = justASecondBeforeTheHour
|
jm.now = justASecondBeforeTheHour
|
||||||
jm.updateCronJob(&cj, &newCj)
|
queue := &fakeQueue{RateLimitingInterface: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "test-update-cronjob")}
|
||||||
|
jm.queue = queue
|
||||||
|
jm.jobControl = &fakeJobControl{}
|
||||||
|
jm.cronJobControl = &fakeCJControl{}
|
||||||
|
jm.recorder = record.NewFakeRecorder(10)
|
||||||
|
|
||||||
if tt.fields.queue.t.Seconds() != tt.deltaTimeForQueue.Seconds() {
|
jm.updateCronJob(tt.oldCronJob, tt.newCronJob)
|
||||||
t.Errorf("Expected %#v got %#v", tt.deltaTimeForQueue.Seconds(), tt.fields.queue.t.Seconds())
|
if queue.delay.Seconds() != tt.expectedDelay.Seconds() {
|
||||||
|
t.Errorf("Expected delay %#v got %#v", tt.expectedDelay.Seconds(), queue.delay.Seconds())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type FakeNamespacedJobLister struct {
|
func TestControllerV2GetJobsToBeReconciled(t *testing.T) {
|
||||||
jobs []*batchv1.Job
|
|
||||||
namespace string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FakeNamespacedJobLister) Get(name string) (*batchv1.Job, error) {
|
|
||||||
for _, j := range f.jobs {
|
|
||||||
if j.Namespace == f.namespace && j.Namespace == name {
|
|
||||||
return j, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("Not Found")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FakeNamespacedJobLister) List(selector labels.Selector) ([]*batchv1.Job, error) {
|
|
||||||
ret := []*batchv1.Job{}
|
|
||||||
for _, j := range f.jobs {
|
|
||||||
if f.namespace != "" && f.namespace != j.Namespace {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if selector.Matches(labels.Set(j.GetLabels())) {
|
|
||||||
ret = append(ret, j)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FakeNamespacedJobLister) Jobs(namespace string) batchv1listers.JobNamespaceLister {
|
|
||||||
f.namespace = namespace
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FakeNamespacedJobLister) GetPodJobs(pod *v1.Pod) (jobs []batchv1.Job, err error) {
|
|
||||||
panic("implement me")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestControllerV2_getJobList(t *testing.T) {
|
|
||||||
trueRef := true
|
trueRef := true
|
||||||
type fields struct {
|
|
||||||
jobLister batchv1listers.JobLister
|
|
||||||
}
|
|
||||||
type args struct {
|
|
||||||
cronJob *batchv1.CronJob
|
|
||||||
}
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
fields fields
|
cronJob *batchv1.CronJob
|
||||||
args args
|
jobs []runtime.Object
|
||||||
want []*batchv1.Job
|
expected []*batchv1.Job
|
||||||
wantErr bool
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "test getting jobs in namespace without controller reference",
|
name: "test getting jobs in namespace without controller reference",
|
||||||
fields: fields{
|
cronJob: &batchv1.CronJob{ObjectMeta: metav1.ObjectMeta{Namespace: "foo-ns", Name: "fooer"}},
|
||||||
&FakeNamespacedJobLister{jobs: []*batchv1.Job{
|
jobs: []runtime.Object{
|
||||||
{
|
&batchv1.Job{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "foo-ns"}},
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "foo-ns"},
|
&batchv1.Job{ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "foo-ns"}},
|
||||||
},
|
&batchv1.Job{ObjectMeta: metav1.ObjectMeta{Name: "foo2", Namespace: "foo-ns"}},
|
||||||
{
|
},
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "foo-ns"},
|
expected: []*batchv1.Job{},
|
||||||
},
|
|
||||||
{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo2", Namespace: "foo-ns"},
|
|
||||||
},
|
|
||||||
}}},
|
|
||||||
args: args{cronJob: &batchv1.CronJob{ObjectMeta: metav1.ObjectMeta{Namespace: "foo-ns", Name: "fooer"}}},
|
|
||||||
want: []*batchv1.Job{},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "test getting jobs in namespace with a controller reference",
|
name: "test getting jobs in namespace with a controller reference",
|
||||||
fields: fields{
|
cronJob: &batchv1.CronJob{ObjectMeta: metav1.ObjectMeta{Namespace: "foo-ns", Name: "fooer"}},
|
||||||
&FakeNamespacedJobLister{jobs: []*batchv1.Job{
|
jobs: []runtime.Object{
|
||||||
{
|
&batchv1.Job{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "foo-ns"}},
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "foo-ns"},
|
&batchv1.Job{ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "foo-ns",
|
||||||
},
|
OwnerReferences: []metav1.OwnerReference{{Name: "fooer", Controller: &trueRef}}}},
|
||||||
{
|
&batchv1.Job{ObjectMeta: metav1.ObjectMeta{Name: "foo2", Namespace: "foo-ns"}},
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "foo-ns",
|
},
|
||||||
OwnerReferences: []metav1.OwnerReference{
|
expected: []*batchv1.Job{
|
||||||
{
|
{ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "foo-ns",
|
||||||
Name: "fooer",
|
OwnerReferences: []metav1.OwnerReference{{Name: "fooer", Controller: &trueRef}}}},
|
||||||
Controller: &trueRef,
|
},
|
||||||
},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo2", Namespace: "foo-ns"},
|
|
||||||
},
|
|
||||||
}}},
|
|
||||||
args: args{cronJob: &batchv1.CronJob{ObjectMeta: metav1.ObjectMeta{Namespace: "foo-ns", Name: "fooer"}}},
|
|
||||||
want: []*batchv1.Job{{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "foo-ns",
|
|
||||||
OwnerReferences: []metav1.OwnerReference{
|
|
||||||
{
|
|
||||||
Name: "fooer",
|
|
||||||
Controller: &trueRef,
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
}},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "test getting jobs in other namespaces ",
|
name: "test getting jobs in other namespaces",
|
||||||
fields: fields{
|
cronJob: &batchv1.CronJob{ObjectMeta: metav1.ObjectMeta{Namespace: "foo-ns", Name: "fooer"}},
|
||||||
&FakeNamespacedJobLister{jobs: []*batchv1.Job{
|
jobs: []runtime.Object{
|
||||||
{
|
&batchv1.Job{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar-ns"}},
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "bar-ns"},
|
&batchv1.Job{ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "bar-ns"}},
|
||||||
},
|
&batchv1.Job{ObjectMeta: metav1.ObjectMeta{Name: "foo2", Namespace: "bar-ns"}},
|
||||||
{
|
},
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "bar-ns"},
|
expected: []*batchv1.Job{},
|
||||||
},
|
|
||||||
{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "foo2", Namespace: "bar-ns"},
|
|
||||||
},
|
|
||||||
}}},
|
|
||||||
args: args{cronJob: &batchv1.CronJob{ObjectMeta: metav1.ObjectMeta{Namespace: "foo-ns", Name: "fooer"}}},
|
|
||||||
want: []*batchv1.Job{},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
jm := &ControllerV2{
|
kubeClient := fake.NewSimpleClientset()
|
||||||
jobLister: tt.fields.jobLister,
|
sharedInformers := informers.NewSharedInformerFactory(kubeClient, controller.NoResyncPeriodFunc())
|
||||||
|
for _, job := range tt.jobs {
|
||||||
|
sharedInformers.Batch().V1().Jobs().Informer().GetIndexer().Add(job)
|
||||||
}
|
}
|
||||||
got, err := jm.getJobsToBeReconciled(tt.args.cronJob)
|
jm, err := NewControllerV2(sharedInformers.Batch().V1().Jobs(), sharedInformers.Batch().V1().CronJobs(), kubeClient)
|
||||||
if (err != nil) != tt.wantErr {
|
if err != nil {
|
||||||
t.Errorf("getJobsToBeReconciled() error = %v, wantErr %v", err, tt.wantErr)
|
t.Errorf("unexpected error %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(got, tt.want) {
|
|
||||||
t.Errorf("getJobsToBeReconciled() got = %v, want %v", got, tt.want)
|
actual, err := jm.getJobsToBeReconciled(tt.cronJob)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(actual, tt.expected) {
|
||||||
|
t.Errorf("\nExpected %#v,\nbut got %#v", tt.expected, actual)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user