2015-09-21 22:51:27 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2015 The Kubernetes Authors .
2015-09-21 22:51:27 +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 .
* /
2016-06-06 22:43:57 +00:00
package podgc
2015-09-21 22:51:27 +00:00
import (
"sync"
"testing"
"time"
2017-06-22 18:24:23 +00:00
"k8s.io/api/core/v1"
2017-01-11 14:09:48 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/sets"
2017-06-23 20:56:37 +00:00
"k8s.io/client-go/informers"
coreinformers "k8s.io/client-go/informers/core/v1"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
2017-02-06 18:35:50 +00:00
"k8s.io/kubernetes/pkg/controller"
2016-11-23 09:33:52 +00:00
"k8s.io/kubernetes/pkg/controller/node/testutil"
2015-09-21 22:51:27 +00:00
)
2016-10-14 12:40:08 +00:00
type FakeController struct { }
func ( * FakeController ) Run ( <- chan struct { } ) { }
func ( * FakeController ) HasSynced ( ) bool {
return true
}
2017-01-12 13:45:53 +00:00
func ( * FakeController ) LastSyncResourceVersion ( ) string {
return ""
}
2017-02-06 18:35:50 +00:00
func alwaysReady ( ) bool { return true }
func NewFromClient ( kubeClient clientset . Interface , terminatedPodThreshold int ) ( * PodGCController , coreinformers . PodInformer ) {
2017-02-08 21:18:21 +00:00
informerFactory := informers . NewSharedInformerFactory ( kubeClient , controller . NoResyncPeriodFunc ( ) )
2017-02-06 18:35:50 +00:00
podInformer := informerFactory . Core ( ) . V1 ( ) . Pods ( )
controller := NewPodGC ( kubeClient , podInformer , terminatedPodThreshold )
controller . podListerSynced = alwaysReady
return controller , podInformer
}
2016-09-12 14:47:17 +00:00
func TestGCTerminated ( t * testing . T ) {
2015-09-25 19:33:33 +00:00
type nameToPhase struct {
name string
2016-11-18 20:50:17 +00:00
phase v1 . PodPhase
2015-09-25 19:33:33 +00:00
}
2015-09-21 22:51:27 +00:00
testCases := [ ] struct {
2015-09-25 19:33:33 +00:00
pods [ ] nameToPhase
2015-09-21 22:51:27 +00:00
threshold int
deletedPodNames sets . String
} {
{
2015-09-25 19:33:33 +00:00
pods : [ ] nameToPhase {
2016-11-18 20:50:17 +00:00
{ name : "a" , phase : v1 . PodFailed } ,
{ name : "b" , phase : v1 . PodSucceeded } ,
2015-09-21 22:51:27 +00:00
} ,
2016-09-12 14:47:17 +00:00
threshold : 0 ,
// threshold = 0 disables terminated pod deletion
deletedPodNames : sets . NewString ( ) ,
} ,
{
pods : [ ] nameToPhase {
2016-11-18 20:50:17 +00:00
{ name : "a" , phase : v1 . PodFailed } ,
{ name : "b" , phase : v1 . PodSucceeded } ,
{ name : "c" , phase : v1 . PodFailed } ,
2016-09-12 14:47:17 +00:00
} ,
threshold : 1 ,
2015-09-21 22:51:27 +00:00
deletedPodNames : sets . NewString ( "a" , "b" ) ,
} ,
2016-09-12 14:47:17 +00:00
{
pods : [ ] nameToPhase {
2016-11-18 20:50:17 +00:00
{ name : "a" , phase : v1 . PodRunning } ,
{ name : "b" , phase : v1 . PodSucceeded } ,
{ name : "c" , phase : v1 . PodFailed } ,
2016-09-12 14:47:17 +00:00
} ,
threshold : 1 ,
deletedPodNames : sets . NewString ( "b" ) ,
} ,
2015-09-21 22:51:27 +00:00
{
2015-09-25 19:33:33 +00:00
pods : [ ] nameToPhase {
2016-11-18 20:50:17 +00:00
{ name : "a" , phase : v1 . PodFailed } ,
{ name : "b" , phase : v1 . PodSucceeded } ,
2015-09-21 22:51:27 +00:00
} ,
threshold : 1 ,
deletedPodNames : sets . NewString ( "a" ) ,
} ,
{
2015-09-25 19:33:33 +00:00
pods : [ ] nameToPhase {
2016-11-18 20:50:17 +00:00
{ name : "a" , phase : v1 . PodFailed } ,
{ name : "b" , phase : v1 . PodSucceeded } ,
2015-09-21 22:51:27 +00:00
} ,
threshold : 5 ,
deletedPodNames : sets . NewString ( ) ,
} ,
}
for i , test := range testCases {
2016-11-23 09:33:52 +00:00
client := fake . NewSimpleClientset ( & v1 . NodeList { Items : [ ] v1 . Node { * testutil . NewNode ( "node" ) } } )
2017-02-06 18:35:50 +00:00
gcc , podInformer := NewFromClient ( client , test . threshold )
2016-09-12 14:47:17 +00:00
deletedPodNames := make ( [ ] string , 0 )
var lock sync . Mutex
gcc . deletePod = func ( _ , name string ) error {
lock . Lock ( )
defer lock . Unlock ( )
deletedPodNames = append ( deletedPodNames , name )
return nil
}
creationTime := time . Unix ( 0 , 0 )
for _ , pod := range test . pods {
creationTime = creationTime . Add ( 1 * time . Hour )
2017-02-06 18:35:50 +00:00
podInformer . Informer ( ) . GetStore ( ) . Add ( & v1 . Pod {
2017-01-17 03:38:19 +00:00
ObjectMeta : metav1 . ObjectMeta { Name : pod . name , CreationTimestamp : metav1 . Time { Time : creationTime } } ,
2016-11-18 20:50:17 +00:00
Status : v1 . PodStatus { Phase : pod . phase } ,
Spec : v1 . PodSpec { NodeName : "node" } ,
2016-09-12 14:47:17 +00:00
} )
}
gcc . gc ( )
pass := true
for _ , pod := range deletedPodNames {
if ! test . deletedPodNames . Has ( pod ) {
pass = false
}
}
if len ( deletedPodNames ) != len ( test . deletedPodNames ) {
pass = false
}
if ! pass {
t . Errorf ( "[%v]pod's deleted expected and actual did not match.\n\texpected: %v\n\tactual: %v" , i , test . deletedPodNames , deletedPodNames )
}
}
}
func TestGCOrphaned ( t * testing . T ) {
type nameToPhase struct {
name string
2016-11-18 20:50:17 +00:00
phase v1 . PodPhase
2016-09-12 14:47:17 +00:00
}
testCases := [ ] struct {
pods [ ] nameToPhase
threshold int
deletedPodNames sets . String
} {
{
pods : [ ] nameToPhase {
2016-11-18 20:50:17 +00:00
{ name : "a" , phase : v1 . PodFailed } ,
{ name : "b" , phase : v1 . PodSucceeded } ,
2016-09-12 14:47:17 +00:00
} ,
threshold : 0 ,
deletedPodNames : sets . NewString ( "a" , "b" ) ,
} ,
{
pods : [ ] nameToPhase {
2016-11-18 20:50:17 +00:00
{ name : "a" , phase : v1 . PodRunning } ,
2016-09-12 14:47:17 +00:00
} ,
threshold : 1 ,
deletedPodNames : sets . NewString ( "a" ) ,
} ,
}
for i , test := range testCases {
client := fake . NewSimpleClientset ( )
2017-02-06 18:35:50 +00:00
gcc , podInformer := NewFromClient ( client , test . threshold )
2015-10-10 00:15:00 +00:00
deletedPodNames := make ( [ ] string , 0 )
var lock sync . Mutex
gcc . deletePod = func ( _ , name string ) error {
lock . Lock ( )
defer lock . Unlock ( )
deletedPodNames = append ( deletedPodNames , name )
return nil
}
2015-09-21 22:51:27 +00:00
creationTime := time . Unix ( 0 , 0 )
2015-09-25 19:33:33 +00:00
for _ , pod := range test . pods {
2015-09-21 22:51:27 +00:00
creationTime = creationTime . Add ( 1 * time . Hour )
2017-02-06 18:35:50 +00:00
podInformer . Informer ( ) . GetStore ( ) . Add ( & v1 . Pod {
2017-01-17 03:38:19 +00:00
ObjectMeta : metav1 . ObjectMeta { Name : pod . name , CreationTimestamp : metav1 . Time { Time : creationTime } } ,
2016-11-18 20:50:17 +00:00
Status : v1 . PodStatus { Phase : pod . phase } ,
Spec : v1 . PodSpec { NodeName : "node" } ,
2015-09-21 22:51:27 +00:00
} )
}
2017-02-06 18:35:50 +00:00
pods , err := podInformer . Lister ( ) . List ( labels . Everything ( ) )
2016-10-25 18:58:13 +00:00
if err != nil {
t . Errorf ( "Error while listing all Pods: %v" , err )
return
}
gcc . gcOrphaned ( pods )
pass := true
for _ , pod := range deletedPodNames {
if ! test . deletedPodNames . Has ( pod ) {
pass = false
}
}
if len ( deletedPodNames ) != len ( test . deletedPodNames ) {
pass = false
}
if ! pass {
t . Errorf ( "[%v]pod's deleted expected and actual did not match.\n\texpected: %v\n\tactual: %v" , i , test . deletedPodNames , deletedPodNames )
}
}
}
func TestGCUnscheduledTerminating ( t * testing . T ) {
type nameToPhase struct {
name string
2016-11-18 20:50:17 +00:00
phase v1 . PodPhase
2016-12-03 18:57:26 +00:00
deletionTimeStamp * metav1 . Time
2016-10-25 18:58:13 +00:00
nodeName string
}
testCases := [ ] struct {
2016-10-27 20:30:04 +00:00
name string
2016-10-25 18:58:13 +00:00
pods [ ] nameToPhase
deletedPodNames sets . String
} {
{
2016-10-27 20:30:04 +00:00
name : "Unscheduled pod in any phase must be deleted" ,
2016-10-25 18:58:13 +00:00
pods : [ ] nameToPhase {
2016-12-03 18:57:26 +00:00
{ name : "a" , phase : v1 . PodFailed , deletionTimeStamp : & metav1 . Time { } , nodeName : "" } ,
{ name : "b" , phase : v1 . PodSucceeded , deletionTimeStamp : & metav1 . Time { } , nodeName : "" } ,
{ name : "c" , phase : v1 . PodRunning , deletionTimeStamp : & metav1 . Time { } , nodeName : "" } ,
2016-10-25 18:58:13 +00:00
} ,
deletedPodNames : sets . NewString ( "a" , "b" , "c" ) ,
} ,
{
2016-10-27 20:30:04 +00:00
name : "Scheduled pod in any phase must not be deleted" ,
2016-10-25 18:58:13 +00:00
pods : [ ] nameToPhase {
2016-11-18 20:50:17 +00:00
{ name : "a" , phase : v1 . PodFailed , deletionTimeStamp : nil , nodeName : "" } ,
{ name : "b" , phase : v1 . PodSucceeded , deletionTimeStamp : nil , nodeName : "node" } ,
2016-12-03 18:57:26 +00:00
{ name : "c" , phase : v1 . PodRunning , deletionTimeStamp : & metav1 . Time { } , nodeName : "node" } ,
2016-10-25 18:58:13 +00:00
} ,
deletedPodNames : sets . NewString ( ) ,
} ,
}
for i , test := range testCases {
client := fake . NewSimpleClientset ( )
2017-02-06 18:35:50 +00:00
gcc , podInformer := NewFromClient ( client , - 1 )
2016-10-25 18:58:13 +00:00
deletedPodNames := make ( [ ] string , 0 )
var lock sync . Mutex
gcc . deletePod = func ( _ , name string ) error {
lock . Lock ( )
defer lock . Unlock ( )
deletedPodNames = append ( deletedPodNames , name )
return nil
}
creationTime := time . Unix ( 0 , 0 )
for _ , pod := range test . pods {
creationTime = creationTime . Add ( 1 * time . Hour )
2017-02-06 18:35:50 +00:00
podInformer . Informer ( ) . GetStore ( ) . Add ( & v1 . Pod {
2017-01-17 03:38:19 +00:00
ObjectMeta : metav1 . ObjectMeta { Name : pod . name , CreationTimestamp : metav1 . Time { Time : creationTime } ,
2016-10-25 18:58:13 +00:00
DeletionTimestamp : pod . deletionTimeStamp } ,
2016-11-18 20:50:17 +00:00
Status : v1 . PodStatus { Phase : pod . phase } ,
Spec : v1 . PodSpec { NodeName : pod . nodeName } ,
2016-10-25 18:58:13 +00:00
} )
}
2017-02-06 18:35:50 +00:00
pods , err := podInformer . Lister ( ) . List ( labels . Everything ( ) )
2016-10-25 18:58:13 +00:00
if err != nil {
t . Errorf ( "Error while listing all Pods: %v" , err )
return
}
gcc . gcUnscheduledTerminating ( pods )
2015-09-21 22:51:27 +00:00
pass := true
2015-10-10 00:15:00 +00:00
for _ , pod := range deletedPodNames {
2015-09-21 22:51:27 +00:00
if ! test . deletedPodNames . Has ( pod ) {
pass = false
}
}
2015-10-10 00:15:00 +00:00
if len ( deletedPodNames ) != len ( test . deletedPodNames ) {
2015-09-21 22:51:27 +00:00
pass = false
}
if ! pass {
2016-10-27 20:30:04 +00:00
t . Errorf ( "[%v]pod's deleted expected and actual did not match.\n\texpected: %v\n\tactual: %v, test: %v" , i , test . deletedPodNames , deletedPodNames , test . name )
2015-09-21 22:51:27 +00:00
}
}
}