2015-03-20 16:37:08 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2014 The Kubernetes Authors .
2015-03-20 16:37:08 +00:00
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 .
* /
2015-09-11 19:22:01 +00:00
package status
2015-03-20 16:37:08 +00:00
import (
2015-06-22 19:31:46 +00:00
"fmt"
2015-03-20 16:37:08 +00:00
"math/rand"
"strconv"
"testing"
2015-05-09 05:01:43 +00:00
"time"
2015-03-20 16:37:08 +00:00
2016-02-05 21:58:03 +00:00
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
2016-02-16 22:16:45 +00:00
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
2016-02-01 22:30:47 +00:00
"k8s.io/kubernetes/pkg/client/testing/core"
2015-10-29 21:04:00 +00:00
"github.com/stretchr/testify/assert"
2015-08-05 22:03:47 +00:00
"k8s.io/kubernetes/pkg/api"
2015-10-24 01:23:47 +00:00
"k8s.io/kubernetes/pkg/api/errors"
2015-09-17 22:21:55 +00:00
"k8s.io/kubernetes/pkg/api/unversioned"
2015-11-10 22:00:12 +00:00
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
2015-10-29 21:04:00 +00:00
kubepod "k8s.io/kubernetes/pkg/kubelet/pod"
2016-02-25 23:40:44 +00:00
podtest "k8s.io/kubernetes/pkg/kubelet/pod/testing"
2015-10-29 21:04:00 +00:00
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
2015-10-24 01:23:47 +00:00
"k8s.io/kubernetes/pkg/runtime"
2015-03-20 16:37:08 +00:00
)
2016-01-15 09:54:58 +00:00
// Generate new instance of test pod with the same initial value.
func getTestPod ( ) * api . Pod {
return & api . Pod {
ObjectMeta : api . ObjectMeta {
UID : "12345678" ,
Name : "foo" ,
Namespace : "new" ,
} ,
}
2015-03-24 23:52:38 +00:00
}
2015-03-20 16:37:08 +00:00
2015-12-09 03:13:09 +00:00
// After adding reconciliation, if status in pod manager is different from the cached status, a reconciliation
// will be triggered, which will mess up all the old unit test.
// To simplify the implementation of unit test, we add testSyncBatch() here, it will make sure the statuses in
// pod manager the same with cached ones before syncBatch() so as to avoid reconciling.
func ( m * manager ) testSyncBatch ( ) {
for uid , status := range m . podStatuses {
pod , ok := m . podManager . GetPodByUID ( uid )
if ok {
pod . Status = status . status
}
pod , ok = m . podManager . GetMirrorPodByPod ( pod )
if ok {
pod . Status = status . status
}
}
m . syncBatch ( )
}
2016-02-01 22:30:47 +00:00
func newTestManager ( kubeClient clientset . Interface ) * manager {
2016-02-25 23:40:44 +00:00
podManager := kubepod . NewBasicPodManager ( podtest . NewFakeMirrorClient ( ) )
2016-01-15 09:54:58 +00:00
podManager . AddPod ( getTestPod ( ) )
2015-10-29 21:04:00 +00:00
return NewManager ( kubeClient , podManager ) . ( * manager )
2015-03-20 16:37:08 +00:00
}
func generateRandomMessage ( ) string {
return strconv . Itoa ( rand . Int ( ) )
}
func getRandomPodStatus ( ) api . PodStatus {
return api . PodStatus {
Message : generateRandomMessage ( ) ,
}
}
2016-02-01 22:30:47 +00:00
func verifyActions ( t * testing . T , kubeClient clientset . Interface , expectedActions [ ] core . Action ) {
actions := kubeClient . ( * fake . Clientset ) . Actions ( )
2015-03-20 16:37:08 +00:00
if len ( actions ) != len ( expectedActions ) {
2016-01-20 00:16:06 +00:00
t . Fatalf ( "unexpected actions, got: %+v expected: %+v" , actions , expectedActions )
2015-03-20 16:37:08 +00:00
return
}
for i := 0 ; i < len ( actions ) ; i ++ {
2015-08-03 13:21:11 +00:00
e := expectedActions [ i ]
a := actions [ i ]
2016-04-13 22:33:15 +00:00
if ! a . Matches ( e . GetVerb ( ) , e . GetResource ( ) . Resource ) || a . GetSubresource ( ) != e . GetSubresource ( ) {
2016-01-20 00:16:06 +00:00
t . Errorf ( "unexpected actions, got: %+v expected: %+v" , actions , expectedActions )
2015-03-20 16:37:08 +00:00
}
}
}
2015-09-11 19:22:01 +00:00
func verifyUpdates ( t * testing . T , manager * manager , expectedUpdates int ) {
2015-03-25 16:32:16 +00:00
// Consume all updates in the channel.
numUpdates := 0
for {
hasUpdate := true
select {
case <- manager . podStatusChannel :
numUpdates ++
default :
hasUpdate = false
}
if ! hasUpdate {
break
}
}
if numUpdates != expectedUpdates {
2015-03-31 22:32:02 +00:00
t . Errorf ( "unexpected number of updates %d, expected %d" , numUpdates , expectedUpdates )
2015-03-25 16:32:16 +00:00
}
}
2015-03-20 16:37:08 +00:00
func TestNewStatus ( t * testing . T ) {
2016-02-01 22:30:47 +00:00
syncer := newTestManager ( & fake . Clientset { } )
2016-01-15 09:54:58 +00:00
testPod := getTestPod ( )
2015-03-24 23:52:38 +00:00
syncer . SetPodStatus ( testPod , getRandomPodStatus ( ) )
2015-03-25 16:32:16 +00:00
verifyUpdates ( t , syncer , 1 )
2015-05-09 05:01:43 +00:00
2015-12-04 23:40:33 +00:00
status := expectPodStatus ( t , syncer , testPod )
2015-05-09 05:01:43 +00:00
if status . StartTime . IsZero ( ) {
t . Errorf ( "SetPodStatus did not set a proper start time value" )
}
}
func TestNewStatusPreservesPodStartTime ( t * testing . T ) {
2016-02-01 22:30:47 +00:00
syncer := newTestManager ( & fake . Clientset { } )
2015-05-09 05:01:43 +00:00
pod := & api . Pod {
ObjectMeta : api . ObjectMeta {
UID : "12345678" ,
Name : "foo" ,
Namespace : "new" ,
} ,
Status : api . PodStatus { } ,
}
2015-09-17 22:21:55 +00:00
now := unversioned . Now ( )
startTime := unversioned . NewTime ( now . Time . Add ( - 1 * time . Minute ) )
2015-05-09 05:01:43 +00:00
pod . Status . StartTime = & startTime
syncer . SetPodStatus ( pod , getRandomPodStatus ( ) )
2015-12-04 23:40:33 +00:00
status := expectPodStatus ( t , syncer , pod )
2015-05-09 05:01:43 +00:00
if ! status . StartTime . Time . Equal ( startTime . Time ) {
t . Errorf ( "Unexpected start time, expected %v, actual %v" , startTime , status . StartTime )
}
2015-03-20 16:37:08 +00:00
}
2015-09-29 20:04:08 +00:00
func getReadyPodStatus ( ) api . PodStatus {
return api . PodStatus {
Conditions : [ ] api . PodCondition {
{
Type : api . PodReady ,
Status : api . ConditionTrue ,
} ,
} ,
}
}
func TestNewStatusSetsReadyTransitionTime ( t * testing . T ) {
2016-02-01 22:30:47 +00:00
syncer := newTestManager ( & fake . Clientset { } )
2015-09-29 20:04:08 +00:00
podStatus := getReadyPodStatus ( )
pod := & api . Pod {
ObjectMeta : api . ObjectMeta {
UID : "12345678" ,
Name : "foo" ,
Namespace : "new" ,
} ,
Status : api . PodStatus { } ,
}
syncer . SetPodStatus ( pod , podStatus )
verifyUpdates ( t , syncer , 1 )
2015-12-04 23:40:33 +00:00
status := expectPodStatus ( t , syncer , pod )
2015-09-29 20:04:08 +00:00
readyCondition := api . GetPodReadyCondition ( status )
if readyCondition . LastTransitionTime . IsZero ( ) {
t . Errorf ( "Unexpected: last transition time not set" )
}
}
2015-03-20 16:37:08 +00:00
func TestChangedStatus ( t * testing . T ) {
2016-02-01 22:30:47 +00:00
syncer := newTestManager ( & fake . Clientset { } )
2016-01-15 09:54:58 +00:00
testPod := getTestPod ( )
2015-03-24 23:52:38 +00:00
syncer . SetPodStatus ( testPod , getRandomPodStatus ( ) )
syncer . SetPodStatus ( testPod , getRandomPodStatus ( ) )
2015-03-25 16:32:16 +00:00
verifyUpdates ( t , syncer , 2 )
2015-03-20 16:37:08 +00:00
}
2015-05-09 05:01:43 +00:00
func TestChangedStatusKeepsStartTime ( t * testing . T ) {
2016-02-01 22:30:47 +00:00
syncer := newTestManager ( & fake . Clientset { } )
2016-01-15 09:54:58 +00:00
testPod := getTestPod ( )
2015-09-17 22:21:55 +00:00
now := unversioned . Now ( )
2015-05-09 05:01:43 +00:00
firstStatus := getRandomPodStatus ( )
firstStatus . StartTime = & now
syncer . SetPodStatus ( testPod , firstStatus )
syncer . SetPodStatus ( testPod , getRandomPodStatus ( ) )
verifyUpdates ( t , syncer , 2 )
2015-12-04 23:40:33 +00:00
finalStatus := expectPodStatus ( t , syncer , testPod )
2015-05-09 05:01:43 +00:00
if finalStatus . StartTime . IsZero ( ) {
t . Errorf ( "StartTime should not be zero" )
}
2016-02-18 02:15:11 +00:00
expected := now . Rfc3339Copy ( )
if ! finalStatus . StartTime . Equal ( expected ) {
t . Errorf ( "Expected %v, but got %v" , expected , finalStatus . StartTime )
2015-05-09 05:01:43 +00:00
}
}
2015-09-29 20:04:08 +00:00
func TestChangedStatusUpdatesLastTransitionTime ( t * testing . T ) {
2016-02-01 22:30:47 +00:00
syncer := newTestManager ( & fake . Clientset { } )
2015-09-29 20:04:08 +00:00
podStatus := getReadyPodStatus ( )
pod := & api . Pod {
ObjectMeta : api . ObjectMeta {
UID : "12345678" ,
Name : "foo" ,
Namespace : "new" ,
} ,
Status : api . PodStatus { } ,
}
syncer . SetPodStatus ( pod , podStatus )
verifyUpdates ( t , syncer , 1 )
2015-12-04 23:40:33 +00:00
oldStatus := expectPodStatus ( t , syncer , pod )
2015-09-29 20:04:08 +00:00
anotherStatus := getReadyPodStatus ( )
anotherStatus . Conditions [ 0 ] . Status = api . ConditionFalse
syncer . SetPodStatus ( pod , anotherStatus )
verifyUpdates ( t , syncer , 1 )
2015-12-04 23:40:33 +00:00
newStatus := expectPodStatus ( t , syncer , pod )
2015-09-29 20:04:08 +00:00
oldReadyCondition := api . GetPodReadyCondition ( oldStatus )
newReadyCondition := api . GetPodReadyCondition ( newStatus )
if newReadyCondition . LastTransitionTime . IsZero ( ) {
t . Errorf ( "Unexpected: last transition time not set" )
}
2015-12-09 03:13:09 +00:00
if newReadyCondition . LastTransitionTime . Before ( oldReadyCondition . LastTransitionTime ) {
t . Errorf ( "Unexpected: new transition time %s, is before old transition time %s" , newReadyCondition . LastTransitionTime , oldReadyCondition . LastTransitionTime )
2015-09-29 20:04:08 +00:00
}
}
2015-03-20 16:37:08 +00:00
func TestUnchangedStatus ( t * testing . T ) {
2016-02-01 22:30:47 +00:00
syncer := newTestManager ( & fake . Clientset { } )
2016-01-15 09:54:58 +00:00
testPod := getTestPod ( )
2015-03-20 16:37:08 +00:00
podStatus := getRandomPodStatus ( )
2015-03-24 23:52:38 +00:00
syncer . SetPodStatus ( testPod , podStatus )
syncer . SetPodStatus ( testPod , podStatus )
2015-03-25 16:32:16 +00:00
verifyUpdates ( t , syncer , 1 )
}
2015-09-29 20:04:08 +00:00
func TestUnchangedStatusPreservesLastTransitionTime ( t * testing . T ) {
2016-02-01 22:30:47 +00:00
syncer := newTestManager ( & fake . Clientset { } )
2015-09-29 20:04:08 +00:00
podStatus := getReadyPodStatus ( )
pod := & api . Pod {
ObjectMeta : api . ObjectMeta {
UID : "12345678" ,
Name : "foo" ,
Namespace : "new" ,
} ,
Status : api . PodStatus { } ,
}
syncer . SetPodStatus ( pod , podStatus )
verifyUpdates ( t , syncer , 1 )
2015-12-04 23:40:33 +00:00
oldStatus := expectPodStatus ( t , syncer , pod )
2015-09-29 20:04:08 +00:00
anotherStatus := getReadyPodStatus ( )
syncer . SetPodStatus ( pod , anotherStatus )
// No update.
verifyUpdates ( t , syncer , 0 )
2015-12-04 23:40:33 +00:00
newStatus := expectPodStatus ( t , syncer , pod )
2015-09-29 20:04:08 +00:00
oldReadyCondition := api . GetPodReadyCondition ( oldStatus )
newReadyCondition := api . GetPodReadyCondition ( newStatus )
if newReadyCondition . LastTransitionTime . IsZero ( ) {
t . Errorf ( "Unexpected: last transition time not set" )
}
if ! oldReadyCondition . LastTransitionTime . Equal ( newReadyCondition . LastTransitionTime ) {
t . Errorf ( "Unexpected: new transition time %s, is not equal to old transition time %s" , newReadyCondition . LastTransitionTime , oldReadyCondition . LastTransitionTime )
}
}
2015-07-28 20:06:05 +00:00
func TestSyncBatchIgnoresNotFound ( t * testing . T ) {
2016-02-01 22:30:47 +00:00
client := fake . Clientset { }
2015-10-29 21:04:00 +00:00
syncer := newTestManager ( & client )
2016-02-01 22:30:47 +00:00
client . AddReactor ( "get" , "pods" , func ( action core . Action ) ( bool , runtime . Object , error ) {
2015-12-10 18:32:29 +00:00
return true , nil , errors . NewNotFound ( api . Resource ( "pods" ) , "test-pod" )
2015-10-24 01:23:47 +00:00
} )
2016-01-15 09:54:58 +00:00
syncer . SetPodStatus ( getTestPod ( ) , getRandomPodStatus ( ) )
2015-12-09 03:13:09 +00:00
syncer . testSyncBatch ( )
2015-10-24 01:23:47 +00:00
2016-02-01 22:30:47 +00:00
verifyActions ( t , syncer . kubeClient , [ ] core . Action {
2016-04-13 22:33:15 +00:00
core . GetActionImpl { ActionImpl : core . ActionImpl { Verb : "get" , Resource : unversioned . GroupVersionResource { Resource : "pods" } } } ,
2015-07-28 20:06:05 +00:00
} )
}
2015-03-25 16:32:16 +00:00
func TestSyncBatch ( t * testing . T ) {
2016-02-01 22:30:47 +00:00
syncer := newTestManager ( & fake . Clientset { } )
2016-01-15 09:54:58 +00:00
testPod := getTestPod ( )
2016-02-01 22:30:47 +00:00
syncer . kubeClient = fake . NewSimpleClientset ( testPod )
2015-03-25 16:32:16 +00:00
syncer . SetPodStatus ( testPod , getRandomPodStatus ( ) )
2015-12-09 03:13:09 +00:00
syncer . testSyncBatch ( )
2016-02-01 22:30:47 +00:00
verifyActions ( t , syncer . kubeClient , [ ] core . Action {
2016-04-13 22:33:15 +00:00
core . GetActionImpl { ActionImpl : core . ActionImpl { Verb : "get" , Resource : unversioned . GroupVersionResource { Resource : "pods" } } } ,
core . UpdateActionImpl { ActionImpl : core . ActionImpl { Verb : "update" , Resource : unversioned . GroupVersionResource { Resource : "pods" } , Subresource : "status" } } ,
2015-08-03 13:21:11 +00:00
} ,
)
2015-03-20 16:37:08 +00:00
}
2015-06-22 19:31:46 +00:00
2015-07-28 20:06:05 +00:00
func TestSyncBatchChecksMismatchedUID ( t * testing . T ) {
2016-02-01 22:30:47 +00:00
syncer := newTestManager ( & fake . Clientset { } )
2016-01-15 09:54:58 +00:00
pod := getTestPod ( )
2015-10-29 21:04:00 +00:00
pod . UID = "first"
2016-01-15 09:54:58 +00:00
syncer . podManager . AddPod ( pod )
differentPod := getTestPod ( )
2015-07-28 20:06:05 +00:00
differentPod . UID = "second"
2016-01-15 09:54:58 +00:00
syncer . podManager . AddPod ( differentPod )
2016-02-01 22:30:47 +00:00
syncer . kubeClient = fake . NewSimpleClientset ( pod )
2016-01-15 09:54:58 +00:00
syncer . SetPodStatus ( differentPod , getRandomPodStatus ( ) )
2015-12-09 03:13:09 +00:00
syncer . testSyncBatch ( )
2016-02-01 22:30:47 +00:00
verifyActions ( t , syncer . kubeClient , [ ] core . Action {
2016-04-13 22:33:15 +00:00
core . GetActionImpl { ActionImpl : core . ActionImpl { Verb : "get" , Resource : unversioned . GroupVersionResource { Resource : "pods" } } } ,
2015-07-28 20:06:05 +00:00
} )
}
2015-10-24 01:23:47 +00:00
func TestSyncBatchNoDeadlock ( t * testing . T ) {
2016-02-01 22:30:47 +00:00
client := & fake . Clientset { }
2015-10-29 21:04:00 +00:00
m := newTestManager ( client )
2016-01-15 09:54:58 +00:00
pod := getTestPod ( )
2015-10-24 01:23:47 +00:00
// Setup fake client.
var ret api . Pod
var err error
2016-02-01 22:30:47 +00:00
client . AddReactor ( "*" , "pods" , func ( action core . Action ) ( bool , runtime . Object , error ) {
2015-10-29 21:04:00 +00:00
switch action := action . ( type ) {
2016-02-01 22:30:47 +00:00
case core . GetAction :
2016-01-15 09:54:58 +00:00
assert . Equal ( t , pod . Name , action . GetName ( ) , "Unexpeted GetAction: %+v" , action )
2016-02-01 22:30:47 +00:00
case core . UpdateAction :
2016-01-15 09:54:58 +00:00
assert . Equal ( t , pod . Name , action . GetObject ( ) . ( * api . Pod ) . Name , "Unexpeted UpdateAction: %+v" , action )
2015-10-29 21:04:00 +00:00
default :
assert . Fail ( t , "Unexpected Action: %+v" , action )
}
2015-10-24 01:23:47 +00:00
return true , & ret , err
} )
pod . Status . ContainerStatuses = [ ] api . ContainerStatus { { State : api . ContainerState { Running : & api . ContainerStateRunning { } } } }
2016-04-13 22:33:15 +00:00
getAction := core . GetActionImpl { ActionImpl : core . ActionImpl { Verb : "get" , Resource : unversioned . GroupVersionResource { Resource : "pods" } } }
updateAction := core . UpdateActionImpl { ActionImpl : core . ActionImpl { Verb : "update" , Resource : unversioned . GroupVersionResource { Resource : "pods" } , Subresource : "status" } }
2015-10-24 01:23:47 +00:00
// Pod not found.
ret = * pod
2015-12-10 18:32:29 +00:00
err = errors . NewNotFound ( api . Resource ( "pods" ) , pod . Name )
2015-10-24 01:23:47 +00:00
m . SetPodStatus ( pod , getRandomPodStatus ( ) )
2015-12-09 03:13:09 +00:00
m . testSyncBatch ( )
2016-02-01 22:30:47 +00:00
verifyActions ( t , client , [ ] core . Action { getAction } )
2015-10-24 01:23:47 +00:00
client . ClearActions ( )
// Pod was recreated.
ret . UID = "other_pod"
err = nil
m . SetPodStatus ( pod , getRandomPodStatus ( ) )
2015-12-09 03:13:09 +00:00
m . testSyncBatch ( )
2016-02-01 22:30:47 +00:00
verifyActions ( t , client , [ ] core . Action { getAction } )
2015-10-24 01:23:47 +00:00
client . ClearActions ( )
// Pod not deleted (success case).
ret = * pod
m . SetPodStatus ( pod , getRandomPodStatus ( ) )
2015-12-09 03:13:09 +00:00
m . testSyncBatch ( )
2016-02-01 22:30:47 +00:00
verifyActions ( t , client , [ ] core . Action { getAction , updateAction } )
2015-10-24 01:23:47 +00:00
client . ClearActions ( )
// Pod is terminated, but still running.
pod . DeletionTimestamp = new ( unversioned . Time )
m . SetPodStatus ( pod , getRandomPodStatus ( ) )
2015-12-09 03:13:09 +00:00
m . testSyncBatch ( )
2016-02-01 22:30:47 +00:00
verifyActions ( t , client , [ ] core . Action { getAction , updateAction } )
2015-10-24 01:23:47 +00:00
client . ClearActions ( )
// Pod is terminated successfully.
pod . Status . ContainerStatuses [ 0 ] . State . Running = nil
pod . Status . ContainerStatuses [ 0 ] . State . Terminated = & api . ContainerStateTerminated { }
m . SetPodStatus ( pod , getRandomPodStatus ( ) )
2015-12-09 03:13:09 +00:00
m . testSyncBatch ( )
2016-02-01 22:30:47 +00:00
verifyActions ( t , client , [ ] core . Action { getAction , updateAction } )
2015-10-24 01:23:47 +00:00
client . ClearActions ( )
// Error case.
err = fmt . Errorf ( "intentional test error" )
m . SetPodStatus ( pod , getRandomPodStatus ( ) )
2015-12-09 03:13:09 +00:00
m . testSyncBatch ( )
2016-02-01 22:30:47 +00:00
verifyActions ( t , client , [ ] core . Action { getAction } )
2015-10-24 01:23:47 +00:00
client . ClearActions ( )
}
func TestStaleUpdates ( t * testing . T ) {
2016-01-15 09:54:58 +00:00
pod := getTestPod ( )
2016-02-01 22:30:47 +00:00
client := fake . NewSimpleClientset ( pod )
2015-10-29 21:04:00 +00:00
m := newTestManager ( client )
2015-10-24 01:23:47 +00:00
status := api . PodStatus { Message : "initial status" }
2016-01-15 09:54:58 +00:00
m . SetPodStatus ( pod , status )
2015-10-24 01:23:47 +00:00
status . Message = "first version bump"
2016-01-15 09:54:58 +00:00
m . SetPodStatus ( pod , status )
2015-10-24 01:23:47 +00:00
status . Message = "second version bump"
2016-01-15 09:54:58 +00:00
m . SetPodStatus ( pod , status )
2015-10-24 01:23:47 +00:00
verifyUpdates ( t , m , 3 )
t . Logf ( "First sync pushes latest status." )
2015-12-09 03:13:09 +00:00
m . testSyncBatch ( )
2016-02-01 22:30:47 +00:00
verifyActions ( t , m . kubeClient , [ ] core . Action {
2016-04-13 22:33:15 +00:00
core . GetActionImpl { ActionImpl : core . ActionImpl { Verb : "get" , Resource : unversioned . GroupVersionResource { Resource : "pods" } } } ,
core . UpdateActionImpl { ActionImpl : core . ActionImpl { Verb : "update" , Resource : unversioned . GroupVersionResource { Resource : "pods" } , Subresource : "status" } } ,
2015-10-24 01:23:47 +00:00
} )
client . ClearActions ( )
for i := 0 ; i < 2 ; i ++ {
t . Logf ( "Next 2 syncs should be ignored (%d)." , i )
2015-12-09 03:13:09 +00:00
m . testSyncBatch ( )
2016-02-01 22:30:47 +00:00
verifyActions ( t , m . kubeClient , [ ] core . Action { } )
2015-10-24 01:23:47 +00:00
}
t . Log ( "Unchanged status should not send an update." )
2016-01-15 09:54:58 +00:00
m . SetPodStatus ( pod , status )
2015-10-24 01:23:47 +00:00
verifyUpdates ( t , m , 0 )
t . Log ( "... unless it's stale." )
m . apiStatusVersions [ pod . UID ] = m . apiStatusVersions [ pod . UID ] - 1
2016-01-15 09:54:58 +00:00
m . SetPodStatus ( pod , status )
2015-12-09 03:13:09 +00:00
m . testSyncBatch ( )
2016-02-01 22:30:47 +00:00
verifyActions ( t , m . kubeClient , [ ] core . Action {
2016-04-13 22:33:15 +00:00
core . GetActionImpl { ActionImpl : core . ActionImpl { Verb : "get" , Resource : unversioned . GroupVersionResource { Resource : "pods" } } } ,
core . UpdateActionImpl { ActionImpl : core . ActionImpl { Verb : "update" , Resource : unversioned . GroupVersionResource { Resource : "pods" } , Subresource : "status" } } ,
2015-10-24 01:23:47 +00:00
} )
// Nothing stuck in the pipe.
verifyUpdates ( t , m , 0 )
}
2015-06-22 19:31:46 +00:00
// shuffle returns a new shuffled list of container statuses.
func shuffle ( statuses [ ] api . ContainerStatus ) [ ] api . ContainerStatus {
numStatuses := len ( statuses )
randIndexes := rand . Perm ( numStatuses )
shuffled := make ( [ ] api . ContainerStatus , numStatuses )
for i := 0 ; i < numStatuses ; i ++ {
shuffled [ i ] = statuses [ randIndexes [ i ] ]
}
return shuffled
}
func TestStatusEquality ( t * testing . T ) {
2016-06-06 10:30:56 +00:00
pod := api . Pod {
Spec : api . PodSpec { } ,
}
2015-06-22 19:31:46 +00:00
containerStatus := [ ] api . ContainerStatus { }
for i := 0 ; i < 10 ; i ++ {
s := api . ContainerStatus {
Name : fmt . Sprintf ( "container%d" , i ) ,
}
containerStatus = append ( containerStatus , s )
}
podStatus := api . PodStatus {
ContainerStatuses : containerStatus ,
}
for i := 0 ; i < 10 ; i ++ {
oldPodStatus := api . PodStatus {
ContainerStatuses : shuffle ( podStatus . ContainerStatuses ) ,
}
2016-06-06 10:30:56 +00:00
normalizeStatus ( & pod , & oldPodStatus )
normalizeStatus ( & pod , & podStatus )
2015-06-22 19:31:46 +00:00
if ! isStatusEqual ( & oldPodStatus , & podStatus ) {
2016-02-18 02:15:11 +00:00
t . Fatalf ( "Order of container statuses should not affect normalized equality." )
2015-06-22 19:31:46 +00:00
}
}
}
2015-10-29 21:04:00 +00:00
func TestStaticPodStatus ( t * testing . T ) {
2016-01-15 09:54:58 +00:00
staticPod := getTestPod ( )
2015-10-29 21:04:00 +00:00
staticPod . Annotations = map [ string ] string { kubetypes . ConfigSourceAnnotationKey : "file" }
2016-01-15 09:54:58 +00:00
mirrorPod := getTestPod ( )
2015-10-29 21:04:00 +00:00
mirrorPod . UID = "mirror-12345678"
mirrorPod . Annotations = map [ string ] string {
kubetypes . ConfigSourceAnnotationKey : "api" ,
kubetypes . ConfigMirrorAnnotationKey : "mirror" ,
}
2016-02-01 22:30:47 +00:00
client := fake . NewSimpleClientset ( mirrorPod )
2015-10-29 21:04:00 +00:00
m := newTestManager ( client )
2016-01-15 09:54:58 +00:00
m . podManager . AddPod ( staticPod )
m . podManager . AddPod ( mirrorPod )
2015-10-29 21:04:00 +00:00
// Verify setup.
2016-01-15 09:54:58 +00:00
assert . True ( t , kubepod . IsStaticPod ( staticPod ) , "SetUp error: staticPod" )
assert . True ( t , kubepod . IsMirrorPod ( mirrorPod ) , "SetUp error: mirrorPod" )
2015-10-29 21:04:00 +00:00
assert . Equal ( t , m . podManager . TranslatePodUID ( mirrorPod . UID ) , staticPod . UID )
status := getRandomPodStatus ( )
now := unversioned . Now ( )
status . StartTime = & now
2016-01-15 09:54:58 +00:00
m . SetPodStatus ( staticPod , status )
retrievedStatus := expectPodStatus ( t , m , staticPod )
2016-06-06 10:30:56 +00:00
normalizeStatus ( staticPod , & status )
2015-10-29 21:04:00 +00:00
assert . True ( t , isStatusEqual ( & status , & retrievedStatus ) , "Expected: %+v, Got: %+v" , status , retrievedStatus )
retrievedStatus , _ = m . GetPodStatus ( mirrorPod . UID )
assert . True ( t , isStatusEqual ( & status , & retrievedStatus ) , "Expected: %+v, Got: %+v" , status , retrievedStatus )
// Should translate mirrorPod / staticPod UID.
2015-12-09 03:13:09 +00:00
m . testSyncBatch ( )
2016-02-01 22:30:47 +00:00
verifyActions ( t , m . kubeClient , [ ] core . Action {
2016-04-13 22:33:15 +00:00
core . GetActionImpl { ActionImpl : core . ActionImpl { Verb : "get" , Resource : unversioned . GroupVersionResource { Resource : "pods" } } } ,
core . UpdateActionImpl { ActionImpl : core . ActionImpl { Verb : "update" , Resource : unversioned . GroupVersionResource { Resource : "pods" } , Subresource : "status" } } ,
2015-10-29 21:04:00 +00:00
} )
2016-02-01 22:30:47 +00:00
updateAction := client . Actions ( ) [ 1 ] . ( core . UpdateActionImpl )
2015-10-29 21:04:00 +00:00
updatedPod := updateAction . Object . ( * api . Pod )
assert . Equal ( t , mirrorPod . UID , updatedPod . UID , "Expected mirrorPod (%q), but got %q" , mirrorPod . UID , updatedPod . UID )
assert . True ( t , isStatusEqual ( & status , & updatedPod . Status ) , "Expected: %+v, Got: %+v" , status , updatedPod . Status )
client . ClearActions ( )
2015-11-14 02:09:17 +00:00
// No changes.
2015-12-09 03:13:09 +00:00
m . testSyncBatch ( )
2016-02-01 22:30:47 +00:00
verifyActions ( t , m . kubeClient , [ ] core . Action { } )
2015-11-14 02:09:17 +00:00
// Mirror pod identity changes.
2016-01-15 09:54:58 +00:00
m . podManager . DeletePod ( mirrorPod )
2015-11-14 02:09:17 +00:00
mirrorPod . UID = "new-mirror-pod"
mirrorPod . Status = api . PodStatus { }
2016-01-15 09:54:58 +00:00
m . podManager . AddPod ( mirrorPod )
2016-06-02 01:47:08 +00:00
// Expect no update to mirror pod, since UID has changed.
2015-12-09 03:13:09 +00:00
m . testSyncBatch ( )
2016-02-01 22:30:47 +00:00
verifyActions ( t , m . kubeClient , [ ] core . Action {
2016-04-13 22:33:15 +00:00
core . GetActionImpl { ActionImpl : core . ActionImpl { Verb : "get" , Resource : unversioned . GroupVersionResource { Resource : "pods" } } } ,
2015-10-29 21:04:00 +00:00
} )
}
2015-11-10 22:00:12 +00:00
func TestSetContainerReadiness ( t * testing . T ) {
2016-03-23 23:45:24 +00:00
cID1 := kubecontainer . ContainerID { Type : "test" , ID : "1" }
cID2 := kubecontainer . ContainerID { Type : "test" , ID : "2" }
2015-12-04 23:40:33 +00:00
containerStatuses := [ ] api . ContainerStatus {
{
Name : "c1" ,
ContainerID : cID1 . String ( ) ,
Ready : false ,
} , {
Name : "c2" ,
ContainerID : cID2 . String ( ) ,
Ready : false ,
} ,
2015-11-10 22:00:12 +00:00
}
status := api . PodStatus {
2015-12-04 23:40:33 +00:00
ContainerStatuses : containerStatuses ,
Conditions : [ ] api . PodCondition { {
Type : api . PodReady ,
Status : api . ConditionFalse ,
} } ,
}
2016-01-15 09:54:58 +00:00
pod := getTestPod ( )
2015-12-04 23:40:33 +00:00
pod . Spec . Containers = [ ] api . Container { { Name : "c1" } , { Name : "c2" } }
// Verify expected readiness of containers & pod.
verifyReadiness := func ( step string , status * api . PodStatus , c1Ready , c2Ready , podReady bool ) {
for _ , c := range status . ContainerStatuses {
switch c . ContainerID {
case cID1 . String ( ) :
if c . Ready != c1Ready {
t . Errorf ( "[%s] Expected readiness of c1 to be %v but was %v" , step , c1Ready , c . Ready )
}
case cID2 . String ( ) :
if c . Ready != c2Ready {
t . Errorf ( "[%s] Expected readiness of c2 to be %v but was %v" , step , c2Ready , c . Ready )
}
default :
t . Fatalf ( "[%s] Unexpected container: %+v" , step , c )
}
}
if status . Conditions [ 0 ] . Type != api . PodReady {
t . Fatalf ( "[%s] Unexpected condition: %+v" , step , status . Conditions [ 0 ] )
} else if ready := ( status . Conditions [ 0 ] . Status == api . ConditionTrue ) ; ready != podReady {
t . Errorf ( "[%s] Expected readiness of pod to be %v but was %v" , step , podReady , ready )
}
2015-11-10 22:00:12 +00:00
}
2016-02-01 22:30:47 +00:00
m := newTestManager ( & fake . Clientset { } )
2016-02-12 05:02:31 +00:00
// Add test pod because the container spec has been changed.
m . podManager . AddPod ( pod )
2015-11-10 22:00:12 +00:00
t . Log ( "Setting readiness before status should fail." )
2016-02-12 05:02:31 +00:00
m . SetContainerReadiness ( pod . UID , cID1 , true )
2015-11-10 22:00:12 +00:00
verifyUpdates ( t , m , 0 )
2015-12-04 23:40:33 +00:00
if status , ok := m . GetPodStatus ( pod . UID ) ; ok {
t . Errorf ( "Unexpected PodStatus: %+v" , status )
}
2015-11-10 22:00:12 +00:00
t . Log ( "Setting initial status." )
2015-12-04 23:40:33 +00:00
m . SetPodStatus ( pod , status )
2015-11-10 22:00:12 +00:00
verifyUpdates ( t , m , 1 )
2015-12-04 23:40:33 +00:00
status = expectPodStatus ( t , m , pod )
verifyReadiness ( "initial" , & status , false , false , false )
2015-11-10 22:00:12 +00:00
t . Log ( "Setting unchanged readiness should do nothing." )
2016-02-12 05:02:31 +00:00
m . SetContainerReadiness ( pod . UID , cID1 , false )
2015-11-10 22:00:12 +00:00
verifyUpdates ( t , m , 0 )
2015-12-04 23:40:33 +00:00
status = expectPodStatus ( t , m , pod )
verifyReadiness ( "unchanged" , & status , false , false , false )
t . Log ( "Setting container readiness should generate update but not pod readiness." )
2016-02-12 05:02:31 +00:00
m . SetContainerReadiness ( pod . UID , cID1 , true )
2015-12-04 23:40:33 +00:00
verifyUpdates ( t , m , 1 )
status = expectPodStatus ( t , m , pod )
verifyReadiness ( "c1 ready" , & status , true , false , false )
2015-11-10 22:00:12 +00:00
2015-12-04 23:40:33 +00:00
t . Log ( "Setting both containers to ready should update pod readiness." )
2016-02-12 05:02:31 +00:00
m . SetContainerReadiness ( pod . UID , cID2 , true )
2015-11-10 22:00:12 +00:00
verifyUpdates ( t , m , 1 )
2015-12-04 23:40:33 +00:00
status = expectPodStatus ( t , m , pod )
verifyReadiness ( "all ready" , & status , true , true , true )
2015-11-10 22:00:12 +00:00
t . Log ( "Setting non-existant container readiness should fail." )
2016-03-23 23:45:24 +00:00
m . SetContainerReadiness ( pod . UID , kubecontainer . ContainerID { Type : "test" , ID : "foo" } , true )
2015-11-10 22:00:12 +00:00
verifyUpdates ( t , m , 0 )
2015-12-04 23:40:33 +00:00
status = expectPodStatus ( t , m , pod )
verifyReadiness ( "ignore non-existant" , & status , true , true , true )
2015-11-10 22:00:12 +00:00
}
2015-11-25 02:27:43 +00:00
func TestSyncBatchCleanupVersions ( t * testing . T ) {
2016-02-01 22:30:47 +00:00
m := newTestManager ( & fake . Clientset { } )
2016-01-15 09:54:58 +00:00
testPod := getTestPod ( )
mirrorPod := getTestPod ( )
2015-11-25 02:27:43 +00:00
mirrorPod . UID = "mirror-uid"
mirrorPod . Name = "mirror_pod"
mirrorPod . Annotations = map [ string ] string {
kubetypes . ConfigSourceAnnotationKey : "api" ,
kubetypes . ConfigMirrorAnnotationKey : "mirror" ,
}
// Orphaned pods should be removed.
m . apiStatusVersions [ testPod . UID ] = 100
m . apiStatusVersions [ mirrorPod . UID ] = 200
2015-12-09 03:13:09 +00:00
m . testSyncBatch ( )
2015-11-25 02:27:43 +00:00
if _ , ok := m . apiStatusVersions [ testPod . UID ] ; ok {
t . Errorf ( "Should have cleared status for testPod" )
}
if _ , ok := m . apiStatusVersions [ mirrorPod . UID ] ; ok {
t . Errorf ( "Should have cleared status for mirrorPod" )
}
// Non-orphaned pods should not be removed.
m . SetPodStatus ( testPod , getRandomPodStatus ( ) )
2016-01-15 09:54:58 +00:00
m . podManager . AddPod ( mirrorPod )
2015-11-25 02:27:43 +00:00
staticPod := mirrorPod
staticPod . UID = "static-uid"
staticPod . Annotations = map [ string ] string { kubetypes . ConfigSourceAnnotationKey : "file" }
2016-01-15 09:54:58 +00:00
m . podManager . AddPod ( staticPod )
2015-11-25 02:27:43 +00:00
m . apiStatusVersions [ testPod . UID ] = 100
m . apiStatusVersions [ mirrorPod . UID ] = 200
2015-12-09 03:13:09 +00:00
m . testSyncBatch ( )
2015-11-25 02:27:43 +00:00
if _ , ok := m . apiStatusVersions [ testPod . UID ] ; ! ok {
t . Errorf ( "Should not have cleared status for testPod" )
}
if _ , ok := m . apiStatusVersions [ mirrorPod . UID ] ; ! ok {
t . Errorf ( "Should not have cleared status for mirrorPod" )
}
}
2015-12-04 23:40:33 +00:00
2015-12-09 03:13:09 +00:00
func TestReconcilePodStatus ( t * testing . T ) {
2016-01-15 09:54:58 +00:00
testPod := getTestPod ( )
2016-02-01 22:30:47 +00:00
client := fake . NewSimpleClientset ( testPod )
2015-12-09 03:13:09 +00:00
syncer := newTestManager ( client )
syncer . SetPodStatus ( testPod , getRandomPodStatus ( ) )
// Call syncBatch directly to test reconcile
syncer . syncBatch ( ) // The apiStatusVersions should be set now
podStatus , ok := syncer . GetPodStatus ( testPod . UID )
if ! ok {
2016-06-14 12:04:38 +00:00
t . Fatalf ( "Should find pod status for pod: %#v" , testPod )
2015-12-09 03:13:09 +00:00
}
testPod . Status = podStatus
// If the pod status is the same, a reconciliation is not needed,
// syncBatch should do nothing
syncer . podManager . UpdatePod ( testPod )
if syncer . needsReconcile ( testPod . UID , podStatus ) {
t . Errorf ( "Pod status is the same, a reconciliation is not needed" )
}
client . ClearActions ( )
syncer . syncBatch ( )
2016-02-01 22:30:47 +00:00
verifyActions ( t , client , [ ] core . Action { } )
2015-12-09 03:13:09 +00:00
// If the pod status is the same, only the timestamp is in Rfc3339 format (lower precision without nanosecond),
// a reconciliation is not needed, syncBatch should do nothing.
// The StartTime should have been set in SetPodStatus().
// TODO(random-liu): Remove this later when api becomes consistent for timestamp.
normalizedStartTime := testPod . Status . StartTime . Rfc3339Copy ( )
testPod . Status . StartTime = & normalizedStartTime
syncer . podManager . UpdatePod ( testPod )
if syncer . needsReconcile ( testPod . UID , podStatus ) {
t . Errorf ( "Pod status only differs for timestamp format, a reconciliation is not needed" )
}
client . ClearActions ( )
syncer . syncBatch ( )
2016-02-01 22:30:47 +00:00
verifyActions ( t , client , [ ] core . Action { } )
2015-12-09 03:13:09 +00:00
// If the pod status is different, a reconciliation is needed, syncBatch should trigger an update
testPod . Status = getRandomPodStatus ( )
syncer . podManager . UpdatePod ( testPod )
if ! syncer . needsReconcile ( testPod . UID , podStatus ) {
t . Errorf ( "Pod status is different, a reconciliation is needed" )
}
client . ClearActions ( )
syncer . syncBatch ( )
2016-02-01 22:30:47 +00:00
verifyActions ( t , client , [ ] core . Action {
2016-04-13 22:33:15 +00:00
core . GetActionImpl { ActionImpl : core . ActionImpl { Verb : "get" , Resource : unversioned . GroupVersionResource { Resource : "pods" } } } ,
core . UpdateActionImpl { ActionImpl : core . ActionImpl { Verb : "update" , Resource : unversioned . GroupVersionResource { Resource : "pods" } , Subresource : "status" } } ,
2015-12-09 03:13:09 +00:00
} )
}
2015-12-04 23:40:33 +00:00
func expectPodStatus ( t * testing . T , m * manager , pod * api . Pod ) api . PodStatus {
status , ok := m . GetPodStatus ( pod . UID )
if ! ok {
t . Fatalf ( "Expected PodStatus for %q not found" , pod . UID )
}
return status
}
2016-01-20 00:16:06 +00:00
func TestDeletePods ( t * testing . T ) {
pod := getTestPod ( )
// Set the deletion timestamp.
pod . DeletionTimestamp = new ( unversioned . Time )
2016-02-01 22:30:47 +00:00
client := fake . NewSimpleClientset ( pod )
2016-01-20 00:16:06 +00:00
m := newTestManager ( client )
m . podManager . AddPod ( pod )
status := getRandomPodStatus ( )
now := unversioned . Now ( )
status . StartTime = & now
m . SetPodStatus ( pod , status )
m . testSyncBatch ( )
// Expect to see an delete action.
2016-02-01 22:30:47 +00:00
verifyActions ( t , m . kubeClient , [ ] core . Action {
2016-04-13 22:33:15 +00:00
core . GetActionImpl { ActionImpl : core . ActionImpl { Verb : "get" , Resource : unversioned . GroupVersionResource { Resource : "pods" } } } ,
core . UpdateActionImpl { ActionImpl : core . ActionImpl { Verb : "update" , Resource : unversioned . GroupVersionResource { Resource : "pods" } , Subresource : "status" } } ,
core . DeleteActionImpl { ActionImpl : core . ActionImpl { Verb : "delete" , Resource : unversioned . GroupVersionResource { Resource : "pods" } } } ,
2016-01-20 00:16:06 +00:00
} )
}
func TestDoNotDeleteMirrorPods ( t * testing . T ) {
staticPod := getTestPod ( )
staticPod . Annotations = map [ string ] string { kubetypes . ConfigSourceAnnotationKey : "file" }
mirrorPod := getTestPod ( )
mirrorPod . UID = "mirror-12345678"
mirrorPod . Annotations = map [ string ] string {
kubetypes . ConfigSourceAnnotationKey : "api" ,
kubetypes . ConfigMirrorAnnotationKey : "mirror" ,
}
// Set the deletion timestamp.
mirrorPod . DeletionTimestamp = new ( unversioned . Time )
2016-02-01 22:30:47 +00:00
client := fake . NewSimpleClientset ( mirrorPod )
2016-01-20 00:16:06 +00:00
m := newTestManager ( client )
m . podManager . AddPod ( staticPod )
m . podManager . AddPod ( mirrorPod )
// Verify setup.
assert . True ( t , kubepod . IsStaticPod ( staticPod ) , "SetUp error: staticPod" )
assert . True ( t , kubepod . IsMirrorPod ( mirrorPod ) , "SetUp error: mirrorPod" )
assert . Equal ( t , m . podManager . TranslatePodUID ( mirrorPod . UID ) , staticPod . UID )
status := getRandomPodStatus ( )
now := unversioned . Now ( )
status . StartTime = & now
m . SetPodStatus ( staticPod , status )
m . testSyncBatch ( )
// Expect not to see an delete action.
2016-02-01 22:30:47 +00:00
verifyActions ( t , m . kubeClient , [ ] core . Action {
2016-04-13 22:33:15 +00:00
core . GetActionImpl { ActionImpl : core . ActionImpl { Verb : "get" , Resource : unversioned . GroupVersionResource { Resource : "pods" } } } ,
core . UpdateActionImpl { ActionImpl : core . ActionImpl { Verb : "update" , Resource : unversioned . GroupVersionResource { Resource : "pods" } , Subresource : "status" } } ,
2016-01-20 00:16:06 +00:00
} )
}