2015-09-08 14:50:39 -04:00
/ *
2016-06-02 20:25:58 -04:00
Copyright 2015 The Kubernetes Authors .
2015-09-08 14:50:39 -04: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 .
* /
package daemon
import (
"fmt"
"testing"
2017-01-25 08:13:07 -05:00
"k8s.io/apimachinery/pkg/api/resource"
2017-01-11 09:09:48 -05:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
2017-01-14 01:55:53 -05:00
"k8s.io/apiserver/pkg/storage/names"
2017-01-25 15:07:10 -05:00
core "k8s.io/client-go/testing"
2017-01-24 09:11:51 -05:00
"k8s.io/client-go/tools/cache"
2017-01-30 13:39:54 -05:00
"k8s.io/client-go/tools/record"
2017-01-12 13:17:43 -05:00
"k8s.io/kubernetes/pkg/api"
2015-09-08 14:50:39 -04:00
"k8s.io/kubernetes/pkg/api/testapi"
2016-11-18 15:50:17 -05:00
"k8s.io/kubernetes/pkg/api/v1"
extensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
2016-12-19 16:39:23 -05:00
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake"
2017-02-06 13:35:50 -05:00
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated"
2015-09-08 14:50:39 -04:00
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/securitycontext"
)
var (
simpleDaemonSetLabel = map [ string ] string { "name" : "simple-daemon" , "type" : "production" }
simpleDaemonSetLabel2 = map [ string ] string { "name" : "simple-daemon" , "type" : "test" }
simpleNodeLabel = map [ string ] string { "color" : "blue" , "speed" : "fast" }
simpleNodeLabel2 = map [ string ] string { "color" : "red" , "speed" : "fast" }
2015-10-03 17:03:27 -04:00
alwaysReady = func ( ) bool { return true }
2015-09-08 14:50:39 -04:00
)
2015-10-09 18:49:10 -04:00
func getKey ( ds * extensions . DaemonSet , t * testing . T ) string {
2015-10-03 17:03:27 -04:00
if key , err := controller . KeyFunc ( ds ) ; err != nil {
t . Errorf ( "Unexpected error getting key for ds %v: %v" , ds . Name , err )
return ""
} else {
return key
}
}
2015-10-09 18:49:10 -04:00
func newDaemonSet ( name string ) * extensions . DaemonSet {
return & extensions . DaemonSet {
2016-12-03 13:57:26 -05:00
TypeMeta : metav1 . TypeMeta { APIVersion : testapi . Extensions . GroupVersion ( ) . String ( ) } ,
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-09-08 14:50:39 -04:00
Name : name ,
2017-01-21 22:36:02 -05:00
Namespace : metav1 . NamespaceDefault ,
2015-09-08 14:50:39 -04:00
} ,
2015-10-09 18:49:10 -04:00
Spec : extensions . DaemonSetSpec {
2016-12-03 13:57:26 -05:00
Selector : & metav1 . LabelSelector { MatchLabels : simpleDaemonSetLabel } ,
2016-11-18 15:50:17 -05:00
Template : v1 . PodTemplateSpec {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-09-08 14:50:39 -04:00
Labels : simpleDaemonSetLabel ,
} ,
2016-11-18 15:50:17 -05:00
Spec : v1 . PodSpec {
Containers : [ ] v1 . Container {
2015-09-08 14:50:39 -04:00
{
Image : "foo/bar" ,
2016-11-18 15:50:17 -05:00
TerminationMessagePath : v1 . TerminationMessagePathDefault ,
ImagePullPolicy : v1 . PullIfNotPresent ,
2015-09-08 14:50:39 -04:00
SecurityContext : securitycontext . ValidSecurityContextWithContainerDefaults ( ) ,
} ,
} ,
2016-11-18 15:50:17 -05:00
DNSPolicy : v1 . DNSDefault ,
2015-09-08 14:50:39 -04:00
} ,
} ,
} ,
}
}
2016-11-18 15:50:17 -05:00
func newNode ( name string , label map [ string ] string ) * v1 . Node {
return & v1 . Node {
2017-01-12 13:17:43 -05:00
TypeMeta : metav1 . TypeMeta { APIVersion : api . Registry . GroupOrDie ( v1 . GroupName ) . GroupVersion . String ( ) } ,
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-09-08 14:50:39 -04:00
Name : name ,
Labels : label ,
2017-01-21 22:36:02 -05:00
Namespace : metav1 . NamespaceDefault ,
2015-09-08 14:50:39 -04:00
} ,
2016-11-18 15:50:17 -05:00
Status : v1 . NodeStatus {
Conditions : [ ] v1 . NodeCondition {
{ Type : v1 . NodeReady , Status : v1 . ConditionTrue } ,
2015-10-28 21:01:34 -04:00
} ,
2016-11-18 15:50:17 -05:00
Allocatable : v1 . ResourceList {
v1 . ResourcePods : resource . MustParse ( "100" ) ,
2016-07-01 13:02:51 -04:00
} ,
2015-10-28 21:01:34 -04:00
} ,
2015-09-08 14:50:39 -04:00
}
}
func addNodes ( nodeStore cache . Store , startIndex , numNodes int , label map [ string ] string ) {
for i := startIndex ; i < startIndex + numNodes ; i ++ {
nodeStore . Add ( newNode ( fmt . Sprintf ( "node-%d" , i ) , label ) )
}
}
2016-11-18 15:50:17 -05:00
func newPod ( podName string , nodeName string , label map [ string ] string ) * v1 . Pod {
pod := & v1 . Pod {
2017-01-12 13:17:43 -05:00
TypeMeta : metav1 . TypeMeta { APIVersion : api . Registry . GroupOrDie ( v1 . GroupName ) . GroupVersion . String ( ) } ,
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2015-09-08 14:50:39 -04:00
GenerateName : podName ,
Labels : label ,
2017-01-21 22:36:02 -05:00
Namespace : metav1 . NamespaceDefault ,
2015-09-08 14:50:39 -04:00
} ,
2016-11-18 15:50:17 -05:00
Spec : v1 . PodSpec {
2015-09-08 14:50:39 -04:00
NodeName : nodeName ,
2016-11-18 15:50:17 -05:00
Containers : [ ] v1 . Container {
2015-09-08 14:50:39 -04:00
{
Image : "foo/bar" ,
2016-11-18 15:50:17 -05:00
TerminationMessagePath : v1 . TerminationMessagePathDefault ,
ImagePullPolicy : v1 . PullIfNotPresent ,
2015-09-08 14:50:39 -04:00
SecurityContext : securitycontext . ValidSecurityContextWithContainerDefaults ( ) ,
} ,
} ,
2016-11-18 15:50:17 -05:00
DNSPolicy : v1 . DNSDefault ,
2015-09-08 14:50:39 -04:00
} ,
}
2017-01-14 01:55:53 -05:00
pod . Name = names . SimpleNameGenerator . GenerateName ( podName )
2015-09-08 14:50:39 -04:00
return pod
}
func addPods ( podStore cache . Store , nodeName string , label map [ string ] string , number int ) {
for i := 0 ; i < number ; i ++ {
podStore . Add ( newPod ( fmt . Sprintf ( "%s-" , nodeName ) , nodeName , label ) )
}
}
2017-01-24 17:49:35 -05:00
func addFailedPods ( podStore cache . Store , nodeName string , label map [ string ] string , number int ) {
for i := 0 ; i < number ; i ++ {
pod := newPod ( fmt . Sprintf ( "%s-" , nodeName ) , nodeName , label )
pod . Status = v1 . PodStatus { Phase : v1 . PodFailed }
podStore . Add ( pod )
}
}
2017-02-06 13:35:50 -05:00
type daemonSetsController struct {
* DaemonSetsController
dsStore cache . Store
podStore cache . Store
nodeStore cache . Store
}
2016-09-15 16:27:47 -04:00
2017-02-06 13:35:50 -05:00
func newTestController ( initialObjects ... runtime . Object ) ( * daemonSetsController , * controller . FakePodControl , * fake . Clientset ) {
clientset := fake . NewSimpleClientset ( initialObjects ... )
informerFactory := informers . NewSharedInformerFactory ( nil , clientset , controller . NoResyncPeriodFunc ( ) )
manager := NewDaemonSetsController (
informerFactory . Extensions ( ) . V1beta1 ( ) . DaemonSets ( ) ,
informerFactory . Core ( ) . V1 ( ) . Pods ( ) ,
informerFactory . Core ( ) . V1 ( ) . Nodes ( ) ,
clientset ,
0 ,
)
2017-01-30 13:39:54 -05:00
manager . eventRecorder = record . NewFakeRecorder ( 100 )
2016-09-15 16:27:47 -04:00
2015-10-03 17:03:27 -04:00
manager . podStoreSynced = alwaysReady
2016-09-07 07:39:49 -04:00
manager . nodeStoreSynced = alwaysReady
2016-12-19 16:39:23 -05:00
manager . dsStoreSynced = alwaysReady
2015-09-21 19:30:02 -04:00
podControl := & controller . FakePodControl { }
2015-09-08 14:50:39 -04:00
manager . podControl = podControl
2017-02-06 13:35:50 -05:00
return & daemonSetsController {
manager ,
informerFactory . Extensions ( ) . V1beta1 ( ) . DaemonSets ( ) . Informer ( ) . GetStore ( ) ,
informerFactory . Core ( ) . V1 ( ) . Pods ( ) . Informer ( ) . GetStore ( ) ,
informerFactory . Core ( ) . V1 ( ) . Nodes ( ) . Informer ( ) . GetStore ( ) ,
} , podControl , clientset
2015-09-08 14:50:39 -04:00
}
2015-09-21 19:30:02 -04:00
func validateSyncDaemonSets ( t * testing . T , fakePodControl * controller . FakePodControl , expectedCreates , expectedDeletes int ) {
if len ( fakePodControl . Templates ) != expectedCreates {
t . Errorf ( "Unexpected number of creates. Expected %d, saw %d\n" , expectedCreates , len ( fakePodControl . Templates ) )
2015-09-08 14:50:39 -04:00
}
2015-09-21 19:30:02 -04:00
if len ( fakePodControl . DeletePodName ) != expectedDeletes {
t . Errorf ( "Unexpected number of deletes. Expected %d, saw %d\n" , expectedDeletes , len ( fakePodControl . DeletePodName ) )
2015-09-08 14:50:39 -04:00
}
}
2017-02-06 13:35:50 -05:00
func syncAndValidateDaemonSets ( t * testing . T , manager * daemonSetsController , ds * extensions . DaemonSet , podControl * controller . FakePodControl , expectedCreates , expectedDeletes int ) {
2015-09-08 14:50:39 -04:00
key , err := controller . KeyFunc ( ds )
if err != nil {
t . Errorf ( "Could not get key for daemon." )
}
manager . syncHandler ( key )
validateSyncDaemonSets ( t , podControl , expectedCreates , expectedDeletes )
}
2016-05-19 17:16:34 -04:00
func TestDeleteFinalStateUnknown ( t * testing . T ) {
2016-12-19 16:39:23 -05:00
manager , _ , _ := newTestController ( )
2017-02-06 13:35:50 -05:00
addNodes ( manager . nodeStore , 0 , 1 , nil )
2016-05-19 17:16:34 -04:00
ds := newDaemonSet ( "foo" )
// DeletedFinalStateUnknown should queue the embedded DS if found.
manager . deleteDaemonset ( cache . DeletedFinalStateUnknown { Key : "foo" , Obj : ds } )
enqueuedKey , _ := manager . queue . Get ( )
if enqueuedKey . ( string ) != "default/foo" {
t . Errorf ( "expected delete of DeletedFinalStateUnknown to enqueue the daemonset but found: %#v" , enqueuedKey )
}
}
2015-09-08 14:50:39 -04:00
// DaemonSets without node selectors should launch pods on every node.
func TestSimpleDaemonSetLaunchesPods ( t * testing . T ) {
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2017-02-06 13:35:50 -05:00
addNodes ( manager . nodeStore , 0 , 5 , nil )
2015-09-08 14:50:39 -04:00
ds := newDaemonSet ( "foo" )
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 5 , 0 )
}
2016-03-18 19:14:07 -04:00
// DaemonSets should do nothing if there aren't any nodes
2015-09-08 14:50:39 -04:00
func TestNoNodesDoesNothing ( t * testing . T ) {
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2015-09-08 14:50:39 -04:00
ds := newDaemonSet ( "foo" )
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 0 , 0 )
}
2016-03-18 19:14:07 -04:00
// DaemonSets without node selectors should launch on a single node in a
// single node cluster.
2015-09-08 14:50:39 -04:00
func TestOneNodeDaemonLaunchesPod ( t * testing . T ) {
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2015-09-08 14:50:39 -04:00
manager . nodeStore . Add ( newNode ( "only-node" , nil ) )
ds := newDaemonSet ( "foo" )
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 1 , 0 )
}
2016-02-10 18:38:27 -05:00
// DaemonSets should place onto NotReady nodes
2015-10-28 21:01:34 -04:00
func TestNotReadNodeDaemonDoesNotLaunchPod ( t * testing . T ) {
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2015-10-28 21:01:34 -04:00
node := newNode ( "not-ready" , nil )
2016-11-18 15:50:17 -05:00
node . Status . Conditions = [ ] v1 . NodeCondition {
{ Type : v1 . NodeReady , Status : v1 . ConditionFalse } ,
2015-10-28 21:01:34 -04:00
}
2015-11-16 11:17:37 -05:00
manager . nodeStore . Add ( node )
ds := newDaemonSet ( "foo" )
manager . dsStore . Add ( ds )
2016-02-10 18:38:27 -05:00
syncAndValidateDaemonSets ( t , manager , ds , podControl , 1 , 0 )
2015-11-16 11:17:37 -05:00
}
2016-01-28 01:13:05 -05:00
// DaemonSets should not place onto OutOfDisk nodes
func TestOutOfDiskNodeDaemonDoesNotLaunchPod ( t * testing . T ) {
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2016-01-28 01:13:05 -05:00
node := newNode ( "not-enough-disk" , nil )
2016-11-18 15:50:17 -05:00
node . Status . Conditions = [ ] v1 . NodeCondition { { Type : v1 . NodeOutOfDisk , Status : v1 . ConditionTrue } }
2016-01-28 01:13:05 -05:00
manager . nodeStore . Add ( node )
ds := newDaemonSet ( "foo" )
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 0 , 0 )
}
2016-11-18 15:50:17 -05:00
func resourcePodSpec ( nodeName , memory , cpu string ) v1 . PodSpec {
return v1 . PodSpec {
2016-04-28 00:02:41 -04:00
NodeName : nodeName ,
2016-11-18 15:50:17 -05:00
Containers : [ ] v1 . Container { {
Resources : v1 . ResourceRequirements {
2016-04-28 00:02:41 -04:00
Requests : allocatableResources ( memory , cpu ) ,
2016-01-28 01:13:05 -05:00
} ,
} } ,
}
2016-04-28 00:02:41 -04:00
}
2016-11-18 15:50:17 -05:00
func allocatableResources ( memory , cpu string ) v1 . ResourceList {
return v1 . ResourceList {
v1 . ResourceMemory : resource . MustParse ( memory ) ,
v1 . ResourceCPU : resource . MustParse ( cpu ) ,
v1 . ResourcePods : resource . MustParse ( "100" ) ,
2016-04-28 00:02:41 -04:00
}
}
// DaemonSets should not place onto nodes with insufficient free resource
2016-11-29 04:54:09 -05:00
func TestInsufficientCapacityNodeDaemonDoesNotLaunchPod ( t * testing . T ) {
2016-04-28 00:02:41 -04:00
podSpec := resourcePodSpec ( "too-much-mem" , "75M" , "75m" )
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2016-01-28 01:13:05 -05:00
node := newNode ( "too-much-mem" , nil )
2016-04-28 00:02:41 -04:00
node . Status . Allocatable = allocatableResources ( "100M" , "200m" )
2016-01-28 01:13:05 -05:00
manager . nodeStore . Add ( node )
2017-02-06 13:35:50 -05:00
manager . podStore . Add ( & v1 . Pod {
2016-01-28 01:13:05 -05:00
Spec : podSpec ,
} )
ds := newDaemonSet ( "foo" )
ds . Spec . Template . Spec = podSpec
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 0 , 0 )
}
2016-12-14 17:25:35 -05:00
// DaemonSets should not unschedule a daemonset pod from a node with insufficient free resource
func TestInsufficentCapacityNodeDaemonDoesNotUnscheduleRunningPod ( t * testing . T ) {
podSpec := resourcePodSpec ( "too-much-mem" , "75M" , "75m" )
podSpec . NodeName = "too-much-mem"
manager , podControl , _ := newTestController ( )
node := newNode ( "too-much-mem" , nil )
node . Status . Allocatable = allocatableResources ( "100M" , "200m" )
manager . nodeStore . Add ( node )
2017-02-06 13:35:50 -05:00
manager . podStore . Add ( & v1 . Pod {
2016-12-14 17:25:35 -05:00
Spec : podSpec ,
} )
ds := newDaemonSet ( "foo" )
ds . Spec . Template . Spec = podSpec
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 0 , 0 )
}
2016-11-29 04:54:09 -05:00
func TestSufficientCapacityWithTerminatedPodsDaemonLaunchesPod ( t * testing . T ) {
2016-04-28 00:02:41 -04:00
podSpec := resourcePodSpec ( "too-much-mem" , "75M" , "75m" )
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2016-04-06 14:47:16 -04:00
node := newNode ( "too-much-mem" , nil )
2016-04-28 00:02:41 -04:00
node . Status . Allocatable = allocatableResources ( "100M" , "200m" )
2016-04-06 14:47:16 -04:00
manager . nodeStore . Add ( node )
2017-02-06 13:35:50 -05:00
manager . podStore . Add ( & v1 . Pod {
2016-04-06 14:47:16 -04:00
Spec : podSpec ,
2016-11-18 15:50:17 -05:00
Status : v1 . PodStatus { Phase : v1 . PodSucceeded } ,
2016-04-06 14:47:16 -04:00
} )
ds := newDaemonSet ( "foo" )
ds . Spec . Template . Spec = podSpec
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 1 , 0 )
}
2016-01-28 01:13:05 -05:00
// DaemonSets should place onto nodes with sufficient free resource
2016-11-29 04:54:09 -05:00
func TestSufficientCapacityNodeDaemonLaunchesPod ( t * testing . T ) {
2016-04-28 00:02:41 -04:00
podSpec := resourcePodSpec ( "not-too-much-mem" , "75M" , "75m" )
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2016-01-28 01:13:05 -05:00
node := newNode ( "not-too-much-mem" , nil )
2016-04-28 00:02:41 -04:00
node . Status . Allocatable = allocatableResources ( "200M" , "200m" )
2016-01-28 01:13:05 -05:00
manager . nodeStore . Add ( node )
2017-02-06 13:35:50 -05:00
manager . podStore . Add ( & v1 . Pod {
2016-01-28 01:13:05 -05:00
Spec : podSpec ,
} )
ds := newDaemonSet ( "foo" )
ds . Spec . Template . Spec = podSpec
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 1 , 0 )
}
2016-06-15 09:34:14 -04:00
// DaemonSets not take any actions when being deleted
func TestDontDoAnythingIfBeingDeleted ( t * testing . T ) {
podSpec := resourcePodSpec ( "not-too-much-mem" , "75M" , "75m" )
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2016-06-15 09:34:14 -04:00
node := newNode ( "not-too-much-mem" , nil )
node . Status . Allocatable = allocatableResources ( "200M" , "200m" )
manager . nodeStore . Add ( node )
2017-02-06 13:35:50 -05:00
manager . podStore . Add ( & v1 . Pod {
2016-06-15 09:34:14 -04:00
Spec : podSpec ,
} )
ds := newDaemonSet ( "foo" )
ds . Spec . Template . Spec = podSpec
2016-12-03 13:57:26 -05:00
now := metav1 . Now ( )
2016-06-15 09:34:14 -04:00
ds . DeletionTimestamp = & now
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 0 , 0 )
}
2016-01-28 01:13:05 -05:00
// DaemonSets should not place onto nodes that would cause port conflicts
func TestPortConflictNodeDaemonDoesNotLaunchPod ( t * testing . T ) {
2016-11-18 15:50:17 -05:00
podSpec := v1 . PodSpec {
2016-01-28 01:13:05 -05:00
NodeName : "port-conflict" ,
2016-11-18 15:50:17 -05:00
Containers : [ ] v1 . Container { {
Ports : [ ] v1 . ContainerPort { {
2016-01-28 01:13:05 -05:00
HostPort : 666 ,
} } ,
} } ,
}
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2016-01-28 01:13:05 -05:00
node := newNode ( "port-conflict" , nil )
2015-10-28 21:01:34 -04:00
manager . nodeStore . Add ( node )
2017-02-06 13:35:50 -05:00
manager . podStore . Add ( & v1 . Pod {
2016-01-28 01:13:05 -05:00
Spec : podSpec ,
} )
2016-03-03 01:15:20 -05:00
ds := newDaemonSet ( "foo" )
ds . Spec . Template . Spec = podSpec
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 0 , 0 )
}
// Test that if the node is already scheduled with a pod using a host port
// but belonging to the same daemonset, we don't delete that pod
//
// Issue: https://github.com/kubernetes/kubernetes/issues/22309
func TestPortConflictWithSameDaemonPodDoesNotDeletePod ( t * testing . T ) {
2016-11-18 15:50:17 -05:00
podSpec := v1 . PodSpec {
2016-03-03 01:15:20 -05:00
NodeName : "port-conflict" ,
2016-11-18 15:50:17 -05:00
Containers : [ ] v1 . Container { {
Ports : [ ] v1 . ContainerPort { {
2016-03-03 01:15:20 -05:00
HostPort : 666 ,
} } ,
} } ,
}
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2016-03-03 01:15:20 -05:00
node := newNode ( "port-conflict" , nil )
manager . nodeStore . Add ( node )
2017-02-06 13:35:50 -05:00
manager . podStore . Add ( & v1 . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-03-03 01:15:20 -05:00
Labels : simpleDaemonSetLabel ,
2017-01-21 22:36:02 -05:00
Namespace : metav1 . NamespaceDefault ,
2016-03-03 01:15:20 -05:00
} ,
Spec : podSpec ,
} )
2015-10-28 21:01:34 -04:00
ds := newDaemonSet ( "foo" )
2016-01-28 01:13:05 -05:00
ds . Spec . Template . Spec = podSpec
2015-10-28 21:01:34 -04:00
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 0 , 0 )
}
2016-01-28 01:13:05 -05:00
// DaemonSets should place onto nodes that would not cause port conflicts
func TestNoPortConflictNodeDaemonLaunchesPod ( t * testing . T ) {
2016-11-18 15:50:17 -05:00
podSpec1 := v1 . PodSpec {
2016-01-28 01:13:05 -05:00
NodeName : "no-port-conflict" ,
2016-11-18 15:50:17 -05:00
Containers : [ ] v1 . Container { {
Ports : [ ] v1 . ContainerPort { {
2016-01-28 01:13:05 -05:00
HostPort : 6661 ,
} } ,
} } ,
}
2016-11-18 15:50:17 -05:00
podSpec2 := v1 . PodSpec {
2016-01-28 01:13:05 -05:00
NodeName : "no-port-conflict" ,
2016-11-18 15:50:17 -05:00
Containers : [ ] v1 . Container { {
Ports : [ ] v1 . ContainerPort { {
2016-01-28 01:13:05 -05:00
HostPort : 6662 ,
} } ,
} } ,
}
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2016-01-28 01:13:05 -05:00
node := newNode ( "no-port-conflict" , nil )
manager . nodeStore . Add ( node )
2017-02-06 13:35:50 -05:00
manager . podStore . Add ( & v1 . Pod {
2016-01-28 01:13:05 -05:00
Spec : podSpec1 ,
} )
ds := newDaemonSet ( "foo" )
ds . Spec . Template . Spec = podSpec2
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 1 , 0 )
}
2016-03-18 19:14:07 -04:00
// DaemonSetController should not sync DaemonSets with empty pod selectors.
//
// issue https://github.com/kubernetes/kubernetes/pull/23223
func TestPodIsNotDeletedByDaemonsetWithEmptyLabelSelector ( t * testing . T ) {
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2017-02-06 13:35:50 -05:00
manager . nodeStore . Add ( newNode ( "node1" , nil ) )
2016-03-18 19:14:07 -04:00
// Create pod not controlled by a daemonset.
2017-02-06 13:35:50 -05:00
manager . podStore . Add ( & v1 . Pod {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2016-03-18 19:14:07 -04:00
Labels : map [ string ] string { "bang" : "boom" } ,
2017-01-21 22:36:02 -05:00
Namespace : metav1 . NamespaceDefault ,
2016-03-18 19:14:07 -04:00
} ,
2016-11-18 15:50:17 -05:00
Spec : v1 . PodSpec {
2016-03-18 19:14:07 -04:00
NodeName : "node1" ,
} ,
} )
// Create a misconfigured DaemonSet. An empty pod selector is invalid but could happen
// if we upgrade and make a backwards incompatible change.
//
// The node selector matches no nodes which mimics the behavior of kubectl delete.
//
// The DaemonSet should not schedule pods and should not delete scheduled pods in
// this case even though it's empty pod selector matches all pods. The DaemonSetController
// should detect this misconfiguration and choose not to sync the DaemonSet. We should
// not observe a deletion of the pod on node1.
ds := newDaemonSet ( "foo" )
2016-12-03 13:57:26 -05:00
ls := metav1 . LabelSelector { }
2016-03-18 19:14:07 -04:00
ds . Spec . Selector = & ls
ds . Spec . Template . Spec . NodeSelector = map [ string ] string { "foo" : "bar" }
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 0 , 0 )
}
2015-09-08 14:50:39 -04:00
// Controller should not create pods on nodes which have daemon pods, and should remove excess pods from nodes that have extra pods.
func TestDealsWithExistingPods ( t * testing . T ) {
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2017-02-06 13:35:50 -05:00
addNodes ( manager . nodeStore , 0 , 5 , nil )
addPods ( manager . podStore , "node-1" , simpleDaemonSetLabel , 1 )
addPods ( manager . podStore , "node-2" , simpleDaemonSetLabel , 2 )
addPods ( manager . podStore , "node-3" , simpleDaemonSetLabel , 5 )
addPods ( manager . podStore , "node-4" , simpleDaemonSetLabel2 , 2 )
2015-09-08 14:50:39 -04:00
ds := newDaemonSet ( "foo" )
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 2 , 5 )
}
// Daemon with node selector should launch pods on nodes matching selector.
func TestSelectorDaemonLaunchesPods ( t * testing . T ) {
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2017-02-06 13:35:50 -05:00
addNodes ( manager . nodeStore , 0 , 4 , nil )
addNodes ( manager . nodeStore , 4 , 3 , simpleNodeLabel )
2015-09-08 14:50:39 -04:00
daemon := newDaemonSet ( "foo" )
daemon . Spec . Template . Spec . NodeSelector = simpleNodeLabel
manager . dsStore . Add ( daemon )
syncAndValidateDaemonSets ( t , manager , daemon , podControl , 3 , 0 )
}
// Daemon with node selector should delete pods from nodes that do not satisfy selector.
func TestSelectorDaemonDeletesUnselectedPods ( t * testing . T ) {
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2017-02-06 13:35:50 -05:00
addNodes ( manager . nodeStore , 0 , 5 , nil )
addNodes ( manager . nodeStore , 5 , 5 , simpleNodeLabel )
addPods ( manager . podStore , "node-0" , simpleDaemonSetLabel2 , 2 )
addPods ( manager . podStore , "node-1" , simpleDaemonSetLabel , 3 )
addPods ( manager . podStore , "node-1" , simpleDaemonSetLabel2 , 1 )
addPods ( manager . podStore , "node-4" , simpleDaemonSetLabel , 1 )
2015-09-08 14:50:39 -04:00
daemon := newDaemonSet ( "foo" )
daemon . Spec . Template . Spec . NodeSelector = simpleNodeLabel
manager . dsStore . Add ( daemon )
syncAndValidateDaemonSets ( t , manager , daemon , podControl , 5 , 4 )
}
// DaemonSet with node selector should launch pods on nodes matching selector, but also deal with existing pods on nodes.
func TestSelectorDaemonDealsWithExistingPods ( t * testing . T ) {
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2017-02-06 13:35:50 -05:00
addNodes ( manager . nodeStore , 0 , 5 , nil )
addNodes ( manager . nodeStore , 5 , 5 , simpleNodeLabel )
addPods ( manager . podStore , "node-0" , simpleDaemonSetLabel , 1 )
addPods ( manager . podStore , "node-1" , simpleDaemonSetLabel , 3 )
addPods ( manager . podStore , "node-1" , simpleDaemonSetLabel2 , 2 )
addPods ( manager . podStore , "node-2" , simpleDaemonSetLabel , 4 )
addPods ( manager . podStore , "node-6" , simpleDaemonSetLabel , 13 )
addPods ( manager . podStore , "node-7" , simpleDaemonSetLabel2 , 4 )
addPods ( manager . podStore , "node-9" , simpleDaemonSetLabel , 1 )
addPods ( manager . podStore , "node-9" , simpleDaemonSetLabel2 , 1 )
2015-09-08 14:50:39 -04:00
ds := newDaemonSet ( "foo" )
ds . Spec . Template . Spec . NodeSelector = simpleNodeLabel
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 3 , 20 )
}
// DaemonSet with node selector which does not match any node labels should not launch pods.
func TestBadSelectorDaemonDoesNothing ( t * testing . T ) {
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2017-02-06 13:35:50 -05:00
addNodes ( manager . nodeStore , 0 , 4 , nil )
addNodes ( manager . nodeStore , 4 , 3 , simpleNodeLabel )
2015-09-08 14:50:39 -04:00
ds := newDaemonSet ( "foo" )
ds . Spec . Template . Spec . NodeSelector = simpleNodeLabel2
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 0 , 0 )
}
// DaemonSet with node name should launch pod on node with corresponding name.
func TestNameDaemonSetLaunchesPods ( t * testing . T ) {
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2017-02-06 13:35:50 -05:00
addNodes ( manager . nodeStore , 0 , 5 , nil )
2015-09-08 14:50:39 -04:00
ds := newDaemonSet ( "foo" )
ds . Spec . Template . Spec . NodeName = "node-0"
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 1 , 0 )
}
// DaemonSet with node name that does not exist should not launch pods.
func TestBadNameDaemonSetDoesNothing ( t * testing . T ) {
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2017-02-06 13:35:50 -05:00
addNodes ( manager . nodeStore , 0 , 5 , nil )
2015-09-08 14:50:39 -04:00
ds := newDaemonSet ( "foo" )
ds . Spec . Template . Spec . NodeName = "node-10"
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 0 , 0 )
}
// DaemonSet with node selector, and node name, matching a node, should launch a pod on the node.
func TestNameAndSelectorDaemonSetLaunchesPods ( t * testing . T ) {
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2017-02-06 13:35:50 -05:00
addNodes ( manager . nodeStore , 0 , 4 , nil )
addNodes ( manager . nodeStore , 4 , 3 , simpleNodeLabel )
2015-09-08 14:50:39 -04:00
ds := newDaemonSet ( "foo" )
ds . Spec . Template . Spec . NodeSelector = simpleNodeLabel
ds . Spec . Template . Spec . NodeName = "node-6"
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 1 , 0 )
}
// DaemonSet with node selector that matches some nodes, and node name that matches a different node, should do nothing.
func TestInconsistentNameSelectorDaemonSetDoesNothing ( t * testing . T ) {
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2017-02-06 13:35:50 -05:00
addNodes ( manager . nodeStore , 0 , 4 , nil )
addNodes ( manager . nodeStore , 4 , 3 , simpleNodeLabel )
2015-09-08 14:50:39 -04:00
ds := newDaemonSet ( "foo" )
ds . Spec . Template . Spec . NodeSelector = simpleNodeLabel
ds . Spec . Template . Spec . NodeName = "node-0"
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , 0 , 0 )
}
2015-10-03 17:03:27 -04:00
2016-07-01 13:02:51 -04:00
// Daemon with node affinity should launch pods on nodes matching affinity.
func TestNodeAffinityDaemonLaunchesPods ( t * testing . T ) {
2016-12-19 16:39:23 -05:00
manager , podControl , _ := newTestController ( )
2017-02-06 13:35:50 -05:00
addNodes ( manager . nodeStore , 0 , 4 , nil )
addNodes ( manager . nodeStore , 4 , 3 , simpleNodeLabel )
2016-07-01 13:02:51 -04:00
daemon := newDaemonSet ( "foo" )
2016-11-30 11:51:12 -05:00
daemon . Spec . Template . Spec . Affinity = & v1 . Affinity {
NodeAffinity : & v1 . NodeAffinity {
RequiredDuringSchedulingIgnoredDuringExecution : & v1 . NodeSelector {
NodeSelectorTerms : [ ] v1 . NodeSelectorTerm {
{
MatchExpressions : [ ] v1 . NodeSelectorRequirement {
{
Key : "color" ,
Operator : v1 . NodeSelectorOpIn ,
Values : [ ] string { simpleNodeLabel [ "color" ] } ,
} ,
} ,
} ,
} ,
} ,
} ,
2016-07-01 13:02:51 -04:00
}
manager . dsStore . Add ( daemon )
syncAndValidateDaemonSets ( t , manager , daemon , podControl , 3 , 0 )
}
2016-10-05 03:16:41 -04:00
func TestNumberReadyStatus ( t * testing . T ) {
2016-12-19 16:39:23 -05:00
daemon := newDaemonSet ( "foo" )
manager , podControl , clientset := newTestController ( )
var updated * extensions . DaemonSet
clientset . PrependReactor ( "update" , "daemonsets" , func ( action core . Action ) ( handled bool , ret runtime . Object , err error ) {
if action . GetSubresource ( ) != "status" {
return false , nil , nil
}
if u , ok := action . ( core . UpdateAction ) ; ok {
updated = u . GetObject ( ) . ( * extensions . DaemonSet )
}
return false , nil , nil
} )
2017-02-06 13:35:50 -05:00
addNodes ( manager . nodeStore , 0 , 2 , simpleNodeLabel )
addPods ( manager . podStore , "node-0" , simpleDaemonSetLabel , 1 )
addPods ( manager . podStore , "node-1" , simpleDaemonSetLabel , 1 )
2016-10-05 03:16:41 -04:00
manager . dsStore . Add ( daemon )
syncAndValidateDaemonSets ( t , manager , daemon , podControl , 0 , 0 )
2016-12-19 16:39:23 -05:00
if updated . Status . NumberReady != 0 {
t . Errorf ( "Wrong daemon %s status: %v" , updated . Name , updated . Status )
2016-10-05 03:16:41 -04:00
}
2016-12-03 13:57:26 -05:00
selector , _ := metav1 . LabelSelectorAsSelector ( daemon . Spec . Selector )
2017-02-06 13:35:50 -05:00
daemonPods , _ := manager . podLister . Pods ( daemon . Namespace ) . List ( selector )
2016-10-05 03:16:41 -04:00
for _ , pod := range daemonPods {
2016-11-18 15:50:17 -05:00
condition := v1 . PodCondition { Type : v1 . PodReady , Status : v1 . ConditionTrue }
2016-10-05 03:16:41 -04:00
pod . Status . Conditions = append ( pod . Status . Conditions , condition )
}
syncAndValidateDaemonSets ( t , manager , daemon , podControl , 0 , 0 )
2016-12-19 16:39:23 -05:00
if updated . Status . NumberReady != 2 {
t . Errorf ( "Wrong daemon %s status: %v" , updated . Name , updated . Status )
2016-10-05 03:16:41 -04:00
}
}
2016-12-20 20:35:20 -05:00
func TestObservedGeneration ( t * testing . T ) {
daemon := newDaemonSet ( "foo" )
daemon . Generation = 1
manager , podControl , clientset := newTestController ( )
var updated * extensions . DaemonSet
clientset . PrependReactor ( "update" , "daemonsets" , func ( action core . Action ) ( handled bool , ret runtime . Object , err error ) {
if action . GetSubresource ( ) != "status" {
return false , nil , nil
}
if u , ok := action . ( core . UpdateAction ) ; ok {
updated = u . GetObject ( ) . ( * extensions . DaemonSet )
}
return false , nil , nil
} )
2017-02-06 13:35:50 -05:00
addNodes ( manager . nodeStore , 0 , 1 , simpleNodeLabel )
addPods ( manager . podStore , "node-0" , simpleDaemonSetLabel , 1 )
2016-12-20 20:35:20 -05:00
manager . dsStore . Add ( daemon )
syncAndValidateDaemonSets ( t , manager , daemon , podControl , 0 , 0 )
if updated . Status . ObservedGeneration != daemon . Generation {
t . Errorf ( "Wrong ObservedGeneration for daemon %s in status. Expected %d, got %d" , updated . Name , daemon . Generation , updated . Status . ObservedGeneration )
}
}
2017-01-09 18:50:04 -05:00
2017-01-25 13:27:25 -05:00
// DaemonSet controller should kill all failed pods and create at most 1 pod on every node.
2017-01-24 17:49:35 -05:00
func TestDaemonKillFailedPods ( t * testing . T ) {
tests := [ ] struct {
numFailedPods , numNormalPods , expectedCreates , expectedDeletes int
test string
} {
{ numFailedPods : 0 , numNormalPods : 1 , expectedCreates : 0 , expectedDeletes : 0 , test : "normal (do nothing)" } ,
{ numFailedPods : 0 , numNormalPods : 0 , expectedCreates : 1 , expectedDeletes : 0 , test : "no pods (create 1)" } ,
{ numFailedPods : 1 , numNormalPods : 0 , expectedCreates : 0 , expectedDeletes : 1 , test : "1 failed pod (kill 1), 0 normal pod (create 0; will create in the next sync)" } ,
{ numFailedPods : 1 , numNormalPods : 3 , expectedCreates : 0 , expectedDeletes : 3 , test : "1 failed pod (kill 1), 3 normal pods (kill 2)" } ,
{ numFailedPods : 2 , numNormalPods : 1 , expectedCreates : 0 , expectedDeletes : 2 , test : "2 failed pods (kill 2), 1 normal pod" } ,
}
for _ , test := range tests {
t . Logf ( "test case: %s\n" , test . test )
manager , podControl , _ := newTestController ( )
2017-02-06 13:35:50 -05:00
addNodes ( manager . nodeStore , 0 , 1 , nil )
addFailedPods ( manager . podStore , "node-0" , simpleDaemonSetLabel , test . numFailedPods )
addPods ( manager . podStore , "node-0" , simpleDaemonSetLabel , test . numNormalPods )
2017-01-24 17:49:35 -05:00
ds := newDaemonSet ( "foo" )
manager . dsStore . Add ( ds )
syncAndValidateDaemonSets ( t , manager , ds , podControl , test . expectedCreates , test . expectedDeletes )
}
}
2017-01-09 18:50:04 -05:00
func TestNodeShouldRunDaemonPod ( t * testing . T ) {
cases := [ ] struct {
podsOnNode [ ] * v1 . Pod
ds * extensions . DaemonSet
wantToRun , shouldSchedule , shouldContinueRunning bool
err error
} {
{
ds : & extensions . DaemonSet {
Spec : extensions . DaemonSetSpec {
Selector : & metav1 . LabelSelector { MatchLabels : simpleDaemonSetLabel } ,
Template : v1 . PodTemplateSpec {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2017-01-09 18:50:04 -05:00
Labels : simpleDaemonSetLabel ,
} ,
Spec : resourcePodSpec ( "" , "50M" , "0.5" ) ,
} ,
} ,
} ,
wantToRun : true ,
shouldSchedule : true ,
shouldContinueRunning : true ,
} ,
{
ds : & extensions . DaemonSet {
Spec : extensions . DaemonSetSpec {
Selector : & metav1 . LabelSelector { MatchLabels : simpleDaemonSetLabel } ,
Template : v1 . PodTemplateSpec {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2017-01-09 18:50:04 -05:00
Labels : simpleDaemonSetLabel ,
} ,
Spec : resourcePodSpec ( "" , "200M" , "0.5" ) ,
} ,
} ,
} ,
wantToRun : true ,
shouldSchedule : false ,
shouldContinueRunning : true ,
} ,
{
ds : & extensions . DaemonSet {
Spec : extensions . DaemonSetSpec {
Selector : & metav1 . LabelSelector { MatchLabels : simpleDaemonSetLabel } ,
Template : v1 . PodTemplateSpec {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2017-01-09 18:50:04 -05:00
Labels : simpleDaemonSetLabel ,
} ,
Spec : resourcePodSpec ( "other-node" , "50M" , "0.5" ) ,
} ,
} ,
} ,
wantToRun : false ,
shouldSchedule : false ,
shouldContinueRunning : false ,
} ,
{
podsOnNode : [ ] * v1 . Pod {
{
Spec : v1 . PodSpec {
Containers : [ ] v1 . Container { {
Ports : [ ] v1 . ContainerPort { {
HostPort : 666 ,
} } ,
} } ,
} ,
} ,
} ,
ds : & extensions . DaemonSet {
Spec : extensions . DaemonSetSpec {
Selector : & metav1 . LabelSelector { MatchLabels : simpleDaemonSetLabel } ,
Template : v1 . PodTemplateSpec {
2017-01-16 22:38:19 -05:00
ObjectMeta : metav1 . ObjectMeta {
2017-01-09 18:50:04 -05:00
Labels : simpleDaemonSetLabel ,
} ,
Spec : v1 . PodSpec {
Containers : [ ] v1 . Container { {
Ports : [ ] v1 . ContainerPort { {
HostPort : 666 ,
} } ,
} } ,
} ,
} ,
} ,
} ,
wantToRun : false ,
shouldSchedule : false ,
shouldContinueRunning : false ,
} ,
}
for i , c := range cases {
node := newNode ( "test-node" , nil )
node . Status . Allocatable = allocatableResources ( "100M" , "1" )
manager , _ , _ := newTestController ( )
2017-02-06 13:35:50 -05:00
manager . nodeStore . Add ( node )
2017-01-09 18:50:04 -05:00
for _ , p := range c . podsOnNode {
2017-02-06 13:35:50 -05:00
manager . podStore . Add ( p )
2017-01-09 18:50:04 -05:00
p . Spec . NodeName = "test-node"
}
wantToRun , shouldSchedule , shouldContinueRunning , err := manager . nodeShouldRunDaemonPod ( node , c . ds )
if wantToRun != c . wantToRun {
t . Errorf ( "[%v] expected wantToRun: %v, got: %v" , i , c . wantToRun , wantToRun )
}
if shouldSchedule != c . shouldSchedule {
t . Errorf ( "[%v] expected shouldSchedule: %v, got: %v" , i , c . shouldSchedule , shouldSchedule )
}
if shouldContinueRunning != c . shouldContinueRunning {
t . Errorf ( "[%v] expected shouldContinueRunning: %v, got: %v" , i , c . shouldContinueRunning , shouldContinueRunning )
}
if err != c . err {
t . Errorf ( "[%v] expected err: %v, got: %v" , i , c . err , err )
}
}
}