2015-08-27 08:19:35 -04:00
/ *
Copyright 2015 The Kubernetes Authors All rights reserved .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package job
import (
"fmt"
"testing"
"time"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
2015-11-26 10:54:04 -05:00
"k8s.io/kubernetes/pkg/api/unversioned"
2015-10-09 18:04:41 -04:00
"k8s.io/kubernetes/pkg/apis/extensions"
2016-02-05 16:58:03 -05:00
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
2016-02-16 17:16:45 -05:00
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
2016-02-12 13:58:43 -05:00
"k8s.io/kubernetes/pkg/client/restclient"
2016-01-15 00:00:58 -05:00
"k8s.io/kubernetes/pkg/client/testing/core"
2015-08-27 08:19:35 -04:00
"k8s.io/kubernetes/pkg/client/unversioned/testclient"
"k8s.io/kubernetes/pkg/controller"
2015-10-24 03:54:45 -04:00
"k8s.io/kubernetes/pkg/util/rand"
2016-02-02 05:57:06 -05:00
"k8s.io/kubernetes/pkg/util/wait"
2015-08-27 08:19:35 -04:00
"k8s.io/kubernetes/pkg/watch"
)
var alwaysReady = func ( ) bool { return true }
2015-10-09 18:49:10 -04:00
func newJob ( parallelism , completions int ) * extensions . Job {
2015-12-14 18:26:16 -05:00
j := & extensions . Job {
2015-08-27 08:19:35 -04:00
ObjectMeta : api . ObjectMeta {
Name : "foobar" ,
Namespace : api . NamespaceDefault ,
} ,
2015-10-09 18:49:10 -04:00
Spec : extensions . JobSpec {
2016-02-02 00:34:42 -05:00
Selector : & unversioned . LabelSelector {
2015-10-14 14:04:33 -04:00
MatchLabels : map [ string ] string { "foo" : "bar" } ,
} ,
2015-09-25 15:07:06 -04:00
Template : api . PodTemplateSpec {
2015-08-27 08:19:35 -04:00
ObjectMeta : api . ObjectMeta {
Labels : map [ string ] string {
"foo" : "bar" ,
} ,
} ,
Spec : api . PodSpec {
Containers : [ ] api . Container {
{ Image : "foo/bar" } ,
} ,
} ,
} ,
} ,
}
2015-12-14 18:26:16 -05:00
// Special case: -1 for either completions or parallelism means leave nil (negative is not allowed
// in practice by validation.
if completions >= 0 {
j . Spec . Completions = & completions
} else {
j . Spec . Completions = nil
}
if parallelism >= 0 {
j . Spec . Parallelism = & parallelism
} else {
j . Spec . Parallelism = nil
}
return j
2015-08-27 08:19:35 -04:00
}
2015-10-09 18:49:10 -04:00
func getKey ( job * extensions . Job , t * testing . T ) string {
2015-08-27 08:19:35 -04:00
if key , err := controller . KeyFunc ( job ) ; err != nil {
t . Errorf ( "Unexpected error getting key for job %v: %v" , job . Name , err )
return ""
} else {
return key
}
}
// create count pods with the given phase for the given job
2015-10-09 18:49:10 -04:00
func newPodList ( count int , status api . PodPhase , job * extensions . Job ) [ ] api . Pod {
2015-08-27 08:19:35 -04:00
pods := [ ] api . Pod { }
for i := 0 ; i < count ; i ++ {
newPod := api . Pod {
ObjectMeta : api . ObjectMeta {
2015-10-24 03:54:45 -04:00
Name : fmt . Sprintf ( "pod-%v" , rand . String ( 10 ) ) ,
2015-10-14 14:04:33 -04:00
Labels : job . Spec . Selector . MatchLabels ,
2015-08-27 08:19:35 -04:00
Namespace : job . Namespace ,
} ,
Status : api . PodStatus { Phase : status } ,
}
pods = append ( pods , newPod )
}
return pods
}
func TestControllerSyncJob ( t * testing . T ) {
testCases := map [ string ] struct {
// job setup
2015-09-17 23:13:00 -04:00
parallelism int
completions int
2015-08-27 08:19:35 -04:00
// pod setup
podControllerError error
activePods int
2015-10-08 13:33:39 -04:00
succeededPods int
failedPods int
2015-08-27 08:19:35 -04:00
// expectations
2015-10-08 13:33:39 -04:00
expectedCreations int
expectedDeletions int
expectedActive int
expectedSucceeded int
expectedFailed int
expectedComplete bool
2015-08-27 08:19:35 -04:00
} {
"job start" : {
2015-09-17 23:13:00 -04:00
2 , 5 ,
2015-08-27 08:19:35 -04:00
nil , 0 , 0 , 0 ,
2 , 0 , 2 , 0 , 0 , false ,
} ,
2015-12-14 18:26:16 -05:00
"WQ job start" : {
2 , - 1 ,
nil , 0 , 0 , 0 ,
2 , 0 , 2 , 0 , 0 , false ,
} ,
2015-08-27 08:19:35 -04:00
"correct # of pods" : {
2015-09-17 23:13:00 -04:00
2 , 5 ,
2015-08-27 08:19:35 -04:00
nil , 2 , 0 , 0 ,
0 , 0 , 2 , 0 , 0 , false ,
} ,
2015-12-14 18:26:16 -05:00
"WQ job: correct # of pods" : {
2 , - 1 ,
nil , 2 , 0 , 0 ,
0 , 0 , 2 , 0 , 0 , false ,
} ,
2015-08-27 08:19:35 -04:00
"too few active pods" : {
2015-09-17 23:13:00 -04:00
2 , 5 ,
2015-08-27 08:19:35 -04:00
nil , 1 , 1 , 0 ,
1 , 0 , 2 , 1 , 0 , false ,
} ,
2015-12-14 18:26:16 -05:00
"too few active pods with a dynamic job" : {
2 , - 1 ,
nil , 1 , 0 , 0 ,
1 , 0 , 2 , 0 , 0 , false ,
} ,
2015-08-27 08:19:35 -04:00
"too few active pods, with controller error" : {
2015-09-17 23:13:00 -04:00
2 , 5 ,
2015-08-27 08:19:35 -04:00
fmt . Errorf ( "Fake error" ) , 1 , 1 , 0 ,
0 , 0 , 1 , 1 , 0 , false ,
} ,
"too many active pods" : {
2015-09-17 23:13:00 -04:00
2 , 5 ,
2015-08-27 08:19:35 -04:00
nil , 3 , 0 , 0 ,
0 , 1 , 2 , 0 , 0 , false ,
} ,
"too many active pods, with controller error" : {
2015-09-17 23:13:00 -04:00
2 , 5 ,
2015-08-27 08:19:35 -04:00
fmt . Errorf ( "Fake error" ) , 3 , 0 , 0 ,
0 , 0 , 3 , 0 , 0 , false ,
} ,
2015-09-17 23:13:00 -04:00
"failed pod" : {
2 , 5 ,
2015-08-27 08:19:35 -04:00
nil , 1 , 1 , 1 ,
1 , 0 , 2 , 1 , 1 , false ,
} ,
2015-09-17 23:13:00 -04:00
"job finish" : {
2 , 5 ,
2015-08-27 08:19:35 -04:00
nil , 0 , 5 , 0 ,
0 , 0 , 0 , 5 , 0 , true ,
} ,
2015-12-14 18:26:16 -05:00
"WQ job finishing" : {
2 , - 1 ,
nil , 1 , 1 , 0 ,
0 , 0 , 1 , 1 , 0 , false ,
} ,
"WQ job all finished" : {
2 , - 1 ,
nil , 0 , 2 , 0 ,
0 , 0 , 0 , 2 , 0 , true ,
} ,
"WQ job all finished despite one failure" : {
2 , - 1 ,
nil , 0 , 1 , 1 ,
0 , 0 , 0 , 1 , 1 , true ,
} ,
2015-08-27 08:19:35 -04:00
"more active pods than completions" : {
2015-09-17 23:13:00 -04:00
2 , 5 ,
2015-08-27 08:19:35 -04:00
nil , 10 , 0 , 0 ,
0 , 8 , 2 , 0 , 0 , false ,
} ,
"status change" : {
2015-09-17 23:13:00 -04:00
2 , 5 ,
2015-08-27 08:19:35 -04:00
nil , 2 , 2 , 0 ,
0 , 0 , 2 , 2 , 0 , false ,
} ,
}
for name , tc := range testCases {
// job manager setup
2016-02-12 13:58:43 -05:00
clientset := clientset . NewForConfigOrDie ( & restclient . Config { Host : "" , ContentConfig : restclient . ContentConfig { GroupVersion : testapi . Default . GroupVersion ( ) } } )
2016-01-15 00:00:58 -05:00
manager := NewJobController ( clientset , controller . NoResyncPeriodFunc )
2015-09-21 19:30:02 -04:00
fakePodControl := controller . FakePodControl { Err : tc . podControllerError }
2015-08-27 08:19:35 -04:00
manager . podControl = & fakePodControl
2015-10-30 11:56:11 -04:00
manager . podStoreSynced = alwaysReady
2015-10-09 18:49:10 -04:00
var actual * extensions . Job
manager . updateHandler = func ( job * extensions . Job ) error {
2015-08-27 08:19:35 -04:00
actual = job
return nil
}
// job & pods setup
2015-10-30 11:56:11 -04:00
job := newJob ( tc . parallelism , tc . completions )
2015-08-27 08:19:35 -04:00
manager . jobStore . Store . Add ( job )
for _ , pod := range newPodList ( tc . activePods , api . PodRunning , job ) {
manager . podStore . Store . Add ( & pod )
}
2015-10-08 13:33:39 -04:00
for _ , pod := range newPodList ( tc . succeededPods , api . PodSucceeded , job ) {
2015-08-27 08:19:35 -04:00
manager . podStore . Store . Add ( & pod )
}
2015-10-08 13:33:39 -04:00
for _ , pod := range newPodList ( tc . failedPods , api . PodFailed , job ) {
2015-08-27 08:19:35 -04:00
manager . podStore . Store . Add ( & pod )
}
// run
err := manager . syncJob ( getKey ( job , t ) )
if err != nil {
t . Errorf ( "%s: unexpected error when syncing jobs %v" , err )
}
// validate created/deleted pods
2015-09-21 19:30:02 -04:00
if len ( fakePodControl . Templates ) != tc . expectedCreations {
t . Errorf ( "%s: unexpected number of creates. Expected %d, saw %d\n" , name , tc . expectedCreations , len ( fakePodControl . Templates ) )
2015-08-27 08:19:35 -04:00
}
2015-09-21 19:30:02 -04:00
if len ( fakePodControl . DeletePodName ) != tc . expectedDeletions {
t . Errorf ( "%s: unexpected number of deletes. Expected %d, saw %d\n" , name , tc . expectedDeletions , len ( fakePodControl . DeletePodName ) )
2015-08-27 08:19:35 -04:00
}
// validate status
if actual . Status . Active != tc . expectedActive {
t . Errorf ( "%s: unexpected number of active pods. Expected %d, saw %d\n" , name , tc . expectedActive , actual . Status . Active )
}
2015-10-08 13:33:39 -04:00
if actual . Status . Succeeded != tc . expectedSucceeded {
t . Errorf ( "%s: unexpected number of succeeded pods. Expected %d, saw %d\n" , name , tc . expectedSucceeded , actual . Status . Succeeded )
2015-08-27 08:19:35 -04:00
}
2015-10-08 13:33:39 -04:00
if actual . Status . Failed != tc . expectedFailed {
t . Errorf ( "%s: unexpected number of failed pods. Expected %d, saw %d\n" , name , tc . expectedFailed , actual . Status . Failed )
2015-08-27 08:19:35 -04:00
}
2015-11-26 10:54:04 -05:00
if actual . Status . StartTime == nil {
t . Errorf ( "%s: .status.startTime was not set" , name )
}
2015-08-27 08:19:35 -04:00
// validate conditions
2015-11-26 10:54:04 -05:00
if tc . expectedComplete && ! getCondition ( actual , extensions . JobComplete ) {
t . Errorf ( "%s: expected completion condition. Got %#v" , name , actual . Status . Conditions )
2015-08-27 08:19:35 -04:00
}
}
}
2015-11-26 10:54:04 -05:00
func TestSyncJobPastDeadline ( t * testing . T ) {
testCases := map [ string ] struct {
// job setup
parallelism int
completions int
activeDeadlineSeconds int64
startTime int64
// pod setup
activePods int
succeededPods int
failedPods int
// expectations
expectedDeletions int
expectedActive int
expectedSucceeded int
expectedFailed int
} {
"activeDeadlineSeconds less than single pod execution" : {
1 , 1 , 10 , 15 ,
1 , 0 , 0 ,
1 , 0 , 0 , 1 ,
} ,
"activeDeadlineSeconds bigger than single pod execution" : {
1 , 2 , 10 , 15 ,
1 , 1 , 0 ,
1 , 0 , 1 , 1 ,
} ,
"activeDeadlineSeconds times-out before any pod starts" : {
1 , 1 , 10 , 10 ,
0 , 0 , 0 ,
0 , 0 , 0 , 0 ,
} ,
}
for name , tc := range testCases {
// job manager setup
2016-02-12 13:58:43 -05:00
clientset := clientset . NewForConfigOrDie ( & restclient . Config { Host : "" , ContentConfig : restclient . ContentConfig { GroupVersion : testapi . Default . GroupVersion ( ) } } )
2016-01-15 00:00:58 -05:00
manager := NewJobController ( clientset , controller . NoResyncPeriodFunc )
2015-11-26 10:54:04 -05:00
fakePodControl := controller . FakePodControl { }
manager . podControl = & fakePodControl
manager . podStoreSynced = alwaysReady
var actual * extensions . Job
manager . updateHandler = func ( job * extensions . Job ) error {
actual = job
return nil
}
// job & pods setup
job := newJob ( tc . parallelism , tc . completions )
job . Spec . ActiveDeadlineSeconds = & tc . activeDeadlineSeconds
start := unversioned . Unix ( unversioned . Now ( ) . Time . Unix ( ) - tc . startTime , 0 )
job . Status . StartTime = & start
manager . jobStore . Store . Add ( job )
for _ , pod := range newPodList ( tc . activePods , api . PodRunning , job ) {
manager . podStore . Store . Add ( & pod )
}
for _ , pod := range newPodList ( tc . succeededPods , api . PodSucceeded , job ) {
manager . podStore . Store . Add ( & pod )
}
for _ , pod := range newPodList ( tc . failedPods , api . PodFailed , job ) {
manager . podStore . Store . Add ( & pod )
}
// run
err := manager . syncJob ( getKey ( job , t ) )
if err != nil {
t . Errorf ( "%s: unexpected error when syncing jobs %v" , err )
}
// validate created/deleted pods
if len ( fakePodControl . Templates ) != 0 {
t . Errorf ( "%s: unexpected number of creates. Expected 0, saw %d\n" , name , len ( fakePodControl . Templates ) )
}
if len ( fakePodControl . DeletePodName ) != tc . expectedDeletions {
t . Errorf ( "%s: unexpected number of deletes. Expected %d, saw %d\n" , name , tc . expectedDeletions , len ( fakePodControl . DeletePodName ) )
}
// validate status
if actual . Status . Active != tc . expectedActive {
t . Errorf ( "%s: unexpected number of active pods. Expected %d, saw %d\n" , name , tc . expectedActive , actual . Status . Active )
}
if actual . Status . Succeeded != tc . expectedSucceeded {
t . Errorf ( "%s: unexpected number of succeeded pods. Expected %d, saw %d\n" , name , tc . expectedSucceeded , actual . Status . Succeeded )
}
if actual . Status . Failed != tc . expectedFailed {
t . Errorf ( "%s: unexpected number of failed pods. Expected %d, saw %d\n" , name , tc . expectedFailed , actual . Status . Failed )
}
if actual . Status . StartTime == nil {
t . Errorf ( "%s: .status.startTime was not set" , name )
}
// validate conditions
if ! getCondition ( actual , extensions . JobFailed ) {
t . Errorf ( "%s: expected fail condition. Got %#v" , name , actual . Status . Conditions )
}
}
}
func getCondition ( job * extensions . Job , condition extensions . JobConditionType ) bool {
for _ , v := range job . Status . Conditions {
if v . Type == condition && v . Status == api . ConditionTrue {
return true
}
}
return false
}
func TestSyncPastDeadlineJobFinished ( t * testing . T ) {
2016-02-12 13:58:43 -05:00
clientset := clientset . NewForConfigOrDie ( & restclient . Config { Host : "" , ContentConfig : restclient . ContentConfig { GroupVersion : testapi . Default . GroupVersion ( ) } } )
2016-01-15 00:00:58 -05:00
manager := NewJobController ( clientset , controller . NoResyncPeriodFunc )
2015-11-26 10:54:04 -05:00
fakePodControl := controller . FakePodControl { }
manager . podControl = & fakePodControl
manager . podStoreSynced = alwaysReady
var actual * extensions . Job
manager . updateHandler = func ( job * extensions . Job ) error {
actual = job
return nil
}
job := newJob ( 1 , 1 )
activeDeadlineSeconds := int64 ( 10 )
job . Spec . ActiveDeadlineSeconds = & activeDeadlineSeconds
start := unversioned . Unix ( unversioned . Now ( ) . Time . Unix ( ) - 15 , 0 )
job . Status . StartTime = & start
job . Status . Conditions = append ( job . Status . Conditions , newCondition ( extensions . JobFailed , "DeadlineExceeded" , "Job was active longer than specified deadline" ) )
manager . jobStore . Store . Add ( job )
err := manager . syncJob ( getKey ( job , t ) )
if err != nil {
t . Errorf ( "Unexpected error when syncing jobs %v" , err )
}
if len ( fakePodControl . Templates ) != 0 {
t . Errorf ( "Unexpected number of creates. Expected %d, saw %d\n" , 0 , len ( fakePodControl . Templates ) )
}
if len ( fakePodControl . DeletePodName ) != 0 {
t . Errorf ( "Unexpected number of deletes. Expected %d, saw %d\n" , 0 , len ( fakePodControl . DeletePodName ) )
}
if actual != nil {
t . Error ( "Unexpected job modification" )
}
2016-01-12 08:59:14 -05:00
}
func TestSyncJobComplete ( t * testing . T ) {
2016-02-12 13:58:43 -05:00
clientset := clientset . NewForConfigOrDie ( & restclient . Config { Host : "" , ContentConfig : restclient . ContentConfig { GroupVersion : testapi . Default . GroupVersion ( ) } } )
2016-01-15 00:00:58 -05:00
manager := NewJobController ( clientset , controller . NoResyncPeriodFunc )
2016-01-12 08:59:14 -05:00
fakePodControl := controller . FakePodControl { }
manager . podControl = & fakePodControl
manager . podStoreSynced = alwaysReady
2015-11-26 10:54:04 -05:00
2016-01-12 08:59:14 -05:00
job := newJob ( 1 , 1 )
job . Status . Conditions = append ( job . Status . Conditions , newCondition ( extensions . JobComplete , "" , "" ) )
manager . jobStore . Store . Add ( job )
err := manager . syncJob ( getKey ( job , t ) )
if err != nil {
t . Fatalf ( "Unexpected error when syncing jobs %v" , err )
}
uncastJob , _ , err := manager . jobStore . Store . Get ( job )
if err != nil {
t . Fatalf ( "Unexpected error when trying to get job from the store: %v" , err )
}
actual := uncastJob . ( * extensions . Job )
// Verify that after syncing a complete job, the conditions are the same.
if got , expected := len ( actual . Status . Conditions ) , 1 ; got != expected {
t . Fatalf ( "Unexpected job status conditions amount; expected %d, got %d" , expected , got )
}
2015-11-26 10:54:04 -05:00
}
2015-08-27 08:19:35 -04:00
func TestSyncJobDeleted ( t * testing . T ) {
2016-02-12 13:58:43 -05:00
clientset := clientset . NewForConfigOrDie ( & restclient . Config { Host : "" , ContentConfig : restclient . ContentConfig { GroupVersion : testapi . Default . GroupVersion ( ) } } )
2016-01-15 00:00:58 -05:00
manager := NewJobController ( clientset , controller . NoResyncPeriodFunc )
2015-09-21 19:30:02 -04:00
fakePodControl := controller . FakePodControl { }
2015-08-27 08:19:35 -04:00
manager . podControl = & fakePodControl
manager . podStoreSynced = alwaysReady
2015-10-09 18:49:10 -04:00
manager . updateHandler = func ( job * extensions . Job ) error { return nil }
2015-09-17 23:13:00 -04:00
job := newJob ( 2 , 2 )
2015-08-27 08:19:35 -04:00
err := manager . syncJob ( getKey ( job , t ) )
if err != nil {
t . Errorf ( "Unexpected error when syncing jobs %v" , err )
}
2015-09-21 19:30:02 -04:00
if len ( fakePodControl . Templates ) != 0 {
t . Errorf ( "Unexpected number of creates. Expected %d, saw %d\n" , 0 , len ( fakePodControl . Templates ) )
2015-08-27 08:19:35 -04:00
}
2015-09-21 19:30:02 -04:00
if len ( fakePodControl . DeletePodName ) != 0 {
t . Errorf ( "Unexpected number of deletes. Expected %d, saw %d\n" , 0 , len ( fakePodControl . DeletePodName ) )
2015-08-27 08:19:35 -04:00
}
}
func TestSyncJobUpdateRequeue ( t * testing . T ) {
2016-02-12 13:58:43 -05:00
clientset := clientset . NewForConfigOrDie ( & restclient . Config { Host : "" , ContentConfig : restclient . ContentConfig { GroupVersion : testapi . Default . GroupVersion ( ) } } )
2016-01-15 00:00:58 -05:00
manager := NewJobController ( clientset , controller . NoResyncPeriodFunc )
2015-09-21 19:30:02 -04:00
fakePodControl := controller . FakePodControl { }
2015-08-27 08:19:35 -04:00
manager . podControl = & fakePodControl
manager . podStoreSynced = alwaysReady
2015-10-09 18:49:10 -04:00
manager . updateHandler = func ( job * extensions . Job ) error { return fmt . Errorf ( "Fake error" ) }
2015-09-17 23:13:00 -04:00
job := newJob ( 2 , 2 )
2015-08-27 08:19:35 -04:00
manager . jobStore . Store . Add ( job )
err := manager . syncJob ( getKey ( job , t ) )
if err != nil {
t . Errorf ( "Unxpected error when syncing jobs, got %v" , err )
}
2015-10-01 17:35:58 -04:00
t . Log ( "Waiting for a job in the queue" )
key , _ := manager . queue . Get ( )
expectedKey := getKey ( job , t )
if key != expectedKey {
t . Errorf ( "Expected requeue of job with key %s got %s" , expectedKey , key )
2015-08-27 08:19:35 -04:00
}
}
func TestJobPodLookup ( t * testing . T ) {
2016-02-12 13:58:43 -05:00
clientset := clientset . NewForConfigOrDie ( & restclient . Config { Host : "" , ContentConfig : restclient . ContentConfig { GroupVersion : testapi . Default . GroupVersion ( ) } } )
2016-01-15 00:00:58 -05:00
manager := NewJobController ( clientset , controller . NoResyncPeriodFunc )
2015-08-27 08:19:35 -04:00
manager . podStoreSynced = alwaysReady
testCases := [ ] struct {
2015-10-09 18:49:10 -04:00
job * extensions . Job
2015-08-27 08:19:35 -04:00
pod * api . Pod
expectedName string
} {
// pods without labels don't match any job
{
2015-10-09 18:49:10 -04:00
job : & extensions . Job {
2015-08-27 08:19:35 -04:00
ObjectMeta : api . ObjectMeta { Name : "basic" } ,
} ,
pod : & api . Pod {
ObjectMeta : api . ObjectMeta { Name : "foo1" , Namespace : api . NamespaceAll } ,
} ,
expectedName : "" ,
} ,
// matching labels, different namespace
{
2015-10-09 18:49:10 -04:00
job : & extensions . Job {
2015-08-27 08:19:35 -04:00
ObjectMeta : api . ObjectMeta { Name : "foo" } ,
2015-10-09 18:49:10 -04:00
Spec : extensions . JobSpec {
2016-02-02 00:34:42 -05:00
Selector : & unversioned . LabelSelector {
2015-10-14 14:04:33 -04:00
MatchLabels : map [ string ] string { "foo" : "bar" } ,
} ,
2015-08-27 08:19:35 -04:00
} ,
} ,
pod : & api . Pod {
ObjectMeta : api . ObjectMeta {
Name : "foo2" ,
Namespace : "ns" ,
Labels : map [ string ] string { "foo" : "bar" } ,
} ,
} ,
expectedName : "" ,
} ,
// matching ns and labels returns
{
2015-10-09 18:49:10 -04:00
job : & extensions . Job {
2015-08-27 08:19:35 -04:00
ObjectMeta : api . ObjectMeta { Name : "bar" , Namespace : "ns" } ,
2015-10-09 18:49:10 -04:00
Spec : extensions . JobSpec {
2016-02-02 00:34:42 -05:00
Selector : & unversioned . LabelSelector {
MatchExpressions : [ ] unversioned . LabelSelectorRequirement {
2015-10-14 14:04:33 -04:00
{
Key : "foo" ,
2016-02-02 00:34:42 -05:00
Operator : unversioned . LabelSelectorOpIn ,
2015-10-14 14:04:33 -04:00
Values : [ ] string { "bar" } ,
} ,
} ,
} ,
2015-08-27 08:19:35 -04:00
} ,
} ,
pod : & api . Pod {
ObjectMeta : api . ObjectMeta {
Name : "foo3" ,
Namespace : "ns" ,
Labels : map [ string ] string { "foo" : "bar" } ,
} ,
} ,
expectedName : "bar" ,
} ,
}
for _ , tc := range testCases {
manager . jobStore . Add ( tc . job )
if job := manager . getPodJob ( tc . pod ) ; job != nil {
if tc . expectedName != job . Name {
t . Errorf ( "Got job %+v expected %+v" , job . Name , tc . expectedName )
}
} else if tc . expectedName != "" {
t . Errorf ( "Expected a job %v pod %v, found none" , tc . expectedName , tc . pod . Name )
}
}
}
type FakeJobExpectations struct {
* controller . ControllerExpectations
satisfied bool
expSatisfied func ( )
}
func ( fe FakeJobExpectations ) SatisfiedExpectations ( controllerKey string ) bool {
fe . expSatisfied ( )
return fe . satisfied
}
// TestSyncJobExpectations tests that a pod cannot sneak in between counting active pods
// and checking expectations.
func TestSyncJobExpectations ( t * testing . T ) {
2016-02-12 13:58:43 -05:00
clientset := clientset . NewForConfigOrDie ( & restclient . Config { Host : "" , ContentConfig : restclient . ContentConfig { GroupVersion : testapi . Default . GroupVersion ( ) } } )
2016-01-15 00:00:58 -05:00
manager := NewJobController ( clientset , controller . NoResyncPeriodFunc )
2015-09-21 19:30:02 -04:00
fakePodControl := controller . FakePodControl { }
2015-08-27 08:19:35 -04:00
manager . podControl = & fakePodControl
manager . podStoreSynced = alwaysReady
2015-10-09 18:49:10 -04:00
manager . updateHandler = func ( job * extensions . Job ) error { return nil }
2015-08-27 08:19:35 -04:00
2015-09-17 23:13:00 -04:00
job := newJob ( 2 , 2 )
2015-08-27 08:19:35 -04:00
manager . jobStore . Store . Add ( job )
pods := newPodList ( 2 , api . PodPending , job )
manager . podStore . Store . Add ( & pods [ 0 ] )
manager . expectations = FakeJobExpectations {
controller . NewControllerExpectations ( ) , true , func ( ) {
// If we check active pods before checking expectataions, the job
// will create a new replica because it doesn't see this pod, but
// has fulfilled its expectations.
manager . podStore . Store . Add ( & pods [ 1 ] )
} ,
}
manager . syncJob ( getKey ( job , t ) )
2015-09-21 19:30:02 -04:00
if len ( fakePodControl . Templates ) != 0 {
t . Errorf ( "Unexpected number of creates. Expected %d, saw %d\n" , 0 , len ( fakePodControl . Templates ) )
2015-08-27 08:19:35 -04:00
}
2015-09-21 19:30:02 -04:00
if len ( fakePodControl . DeletePodName ) != 0 {
t . Errorf ( "Unexpected number of deletes. Expected %d, saw %d\n" , 0 , len ( fakePodControl . DeletePodName ) )
2015-08-27 08:19:35 -04:00
}
}
type FakeWatcher struct {
w * watch . FakeWatcher
* testclient . Fake
}
func TestWatchJobs ( t * testing . T ) {
2016-01-15 00:00:58 -05:00
clientset := fake . NewSimpleClientset ( )
2015-08-27 08:19:35 -04:00
fakeWatch := watch . NewFake ( )
2016-01-15 00:00:58 -05:00
clientset . PrependWatchReactor ( "*" , core . DefaultWatchReactor ( fakeWatch , nil ) )
manager := NewJobController ( clientset , controller . NoResyncPeriodFunc )
2015-08-27 08:19:35 -04:00
manager . podStoreSynced = alwaysReady
2015-10-09 18:49:10 -04:00
var testJob extensions . Job
2015-10-01 17:35:58 -04:00
received := make ( chan struct { } )
2015-08-27 08:19:35 -04:00
// The update sent through the fakeWatcher should make its way into the workqueue,
// and eventually into the syncHandler.
manager . syncHandler = func ( key string ) error {
obj , exists , err := manager . jobStore . Store . GetByKey ( key )
if ! exists || err != nil {
t . Errorf ( "Expected to find job under key %v" , key )
}
2015-10-09 18:49:10 -04:00
job := * obj . ( * extensions . Job )
2015-08-27 08:19:35 -04:00
if ! api . Semantic . DeepDerivative ( job , testJob ) {
t . Errorf ( "Expected %#v, but got %#v" , testJob , job )
}
2015-10-01 17:35:58 -04:00
close ( received )
2015-08-27 08:19:35 -04:00
return nil
}
// Start only the job watcher and the workqueue, send a watch event,
// and make sure it hits the sync method.
stopCh := make ( chan struct { } )
defer close ( stopCh )
go manager . jobController . Run ( stopCh )
2016-02-02 05:57:06 -05:00
go wait . Until ( manager . worker , 10 * time . Millisecond , stopCh )
2015-08-27 08:19:35 -04:00
// We're sending new job to see if it reaches syncHandler.
testJob . Name = "foo"
fakeWatch . Add ( & testJob )
2015-10-01 17:35:58 -04:00
t . Log ( "Waiting for job to reach syncHandler" )
<- received
}
2015-08-27 08:19:35 -04:00
2015-10-01 17:35:58 -04:00
func TestIsJobFinished ( t * testing . T ) {
2015-10-09 18:49:10 -04:00
job := & extensions . Job {
Status : extensions . JobStatus {
Conditions : [ ] extensions . JobCondition { {
Type : extensions . JobComplete ,
2015-10-01 17:35:58 -04:00
Status : api . ConditionTrue ,
2015-08-27 08:19:35 -04:00
} } ,
} ,
}
2015-10-01 17:35:58 -04:00
if ! isJobFinished ( job ) {
t . Error ( "Job was expected to be finished" )
}
job . Status . Conditions [ 0 ] . Status = api . ConditionFalse
if isJobFinished ( job ) {
t . Error ( "Job was not expected to be finished" )
}
job . Status . Conditions [ 0 ] . Status = api . ConditionUnknown
if isJobFinished ( job ) {
t . Error ( "Job was not expected to be finished" )
2015-08-27 08:19:35 -04:00
}
}
func TestWatchPods ( t * testing . T ) {
2016-01-15 00:00:58 -05:00
clientset := fake . NewSimpleClientset ( )
2015-08-27 08:19:35 -04:00
fakeWatch := watch . NewFake ( )
2016-01-15 00:00:58 -05:00
clientset . PrependWatchReactor ( "*" , core . DefaultWatchReactor ( fakeWatch , nil ) )
manager := NewJobController ( clientset , controller . NoResyncPeriodFunc )
2015-08-27 08:19:35 -04:00
manager . podStoreSynced = alwaysReady
// Put one job and one pod into the store
2015-09-17 23:13:00 -04:00
testJob := newJob ( 2 , 2 )
2015-08-27 08:19:35 -04:00
manager . jobStore . Store . Add ( testJob )
2015-10-01 17:35:58 -04:00
received := make ( chan struct { } )
2015-08-27 08:19:35 -04:00
// The pod update sent through the fakeWatcher should figure out the managing job and
// send it into the syncHandler.
manager . syncHandler = func ( key string ) error {
obj , exists , err := manager . jobStore . Store . GetByKey ( key )
if ! exists || err != nil {
t . Errorf ( "Expected to find job under key %v" , key )
}
2015-10-09 18:49:10 -04:00
job := obj . ( * extensions . Job )
2015-08-27 08:19:35 -04:00
if ! api . Semantic . DeepDerivative ( job , testJob ) {
t . Errorf ( "\nExpected %#v,\nbut got %#v" , testJob , job )
}
close ( received )
return nil
}
// Start only the pod watcher and the workqueue, send a watch event,
// and make sure it hits the sync method for the right job.
stopCh := make ( chan struct { } )
defer close ( stopCh )
go manager . podController . Run ( stopCh )
2016-02-02 05:57:06 -05:00
go wait . Until ( manager . worker , 10 * time . Millisecond , stopCh )
2015-08-27 08:19:35 -04:00
pods := newPodList ( 1 , api . PodRunning , testJob )
testPod := pods [ 0 ]
testPod . Status . Phase = api . PodFailed
fakeWatch . Add ( & testPod )
2015-10-01 17:35:58 -04:00
t . Log ( "Waiting for pod to reach syncHandler" )
<- received
2015-08-27 08:19:35 -04:00
}