Skip to content

Commit e9a9e7a

Browse files
authored
chore(tests): improve Pod scheduler tests (#1085)
1 parent 4d7591c commit e9a9e7a

3 files changed

Lines changed: 114 additions & 3 deletions

File tree

rootfs/scheduler/mock.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,7 @@ def post(request, context):
684684
# don't bother adding it to those two resources since they live outside namespace
685685
if resource_type not in ['nodes', 'namespaces']:
686686
namespace = request.url.replace(settings.SCHEDULER_URL + '/api/v1/namespaces/', '')
687-
namespace = request.url.replace(settings.SCHEDULER_URL + '/apis/extensions/v1beta1/namespaces/', '') # noqa
687+
namespace = namespace.replace(settings.SCHEDULER_URL + '/apis/extensions/v1beta1/namespaces/', '') # noqa
688688
namespace = namespace.split('/')[0]
689689
data['metadata']['namespace'] = namespace
690690

rootfs/scheduler/resources/pod.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ def manifest(self, namespace, name, image, **kwargs):
113113
'apiVersion': 'v1',
114114
'metadata': {
115115
'name': name,
116+
'namespace': namespace,
116117
'labels': labels
117118
},
118119
'spec': {}
@@ -381,7 +382,7 @@ def readiness_status(self, pod):
381382
"""Check if the pod container have passed the readiness probes"""
382383
name = '{}-{}'.format(pod['metadata']['labels']['app'], pod['metadata']['labels']['type'])
383384
# find the right container in case there are many on the pod
384-
container = self.pod.find_container(name, pod['status']['containerStatuses'])
385+
container = self.find_container(name, pod['status']['containerStatuses'])
385386
if container is None:
386387
# Seems like the most sensible default
387388
return 'Unknown'
@@ -679,9 +680,10 @@ def _handle_not_ready_pods(self, namespace, labels):
679680
# only care about pods that are in running phase
680681
if pod['status']['phase'] != 'Running':
681682
continue
683+
682684
name = '{}-{}'.format(pod['metadata']['labels']['app'], pod['metadata']['labels']['type']) # noqa
683685
# find the right container in case there are many on the pod
684-
container = self.pod.find_container(name, pod['status']['containerStatuses'])
686+
container = self.find_container(name, pod['status']['containerStatuses'])
685687
if container is None or container['ready'] == 'true':
686688
continue
687689

@@ -690,4 +692,5 @@ def _handle_not_ready_pods(self, namespace, labels):
690692
# strip out whitespaces on either side
691693
message = "\n".join([x.strip() for x in event['message'].split("\n")])
692694
raise KubeException(message)
695+
693696
return None

rootfs/scheduler/tests/test_pods.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
44
Run the tests with './manage.py test scheduler'
55
"""
6+
from unittest import mock
7+
from datetime import datetime, timedelta
68
from scheduler import KubeHTTPException
79
from scheduler.tests import TestCase
810
from scheduler.utils import generate_random_name
@@ -91,3 +93,109 @@ def test_get_pod(self):
9193
},
9294
data['metadata']['labels']
9395
)
96+
97+
def test_liveness_status(self):
98+
# Missing Ready type means pod has passed liveness check
99+
pod = {'status': {'conditions': []}}
100+
self.assertTrue(self.scheduler.pod.liveness_status(pod))
101+
102+
# fake out a minimal pod structure for success
103+
pod['status']['conditions'].append({
104+
'type': 'Ready',
105+
'status': 'True'
106+
})
107+
self.assertTrue(self.scheduler.pod.liveness_status(pod))
108+
109+
# fake out a minimal pod structure for failure
110+
pod['status']['conditions'][0]['status'] = 'False'
111+
self.assertFalse(self.scheduler.pod.liveness_status(pod))
112+
113+
def test_readiness_status(self):
114+
# create a pod to then manipulate
115+
name = self.create()
116+
pod = self.scheduler.pod.get(self.namespace, name).json()
117+
118+
# on a newly created pod has an overall "Running" status with ready state on a container
119+
self.assertEqual(self.scheduler.pod.readiness_status(pod), 'Running')
120+
121+
# on a newly created pod has been deleted with ready state on a container
122+
pod['metadata']['deletionTimestamp'] = 'fake'
123+
self.assertEqual(self.scheduler.pod.readiness_status(pod), 'Terminating')
124+
del pod['metadata']['deletionTimestamp']
125+
126+
# now say the app container is not Ready
127+
container = pod['status']['containerStatuses'][0]
128+
container['ready'] = False
129+
130+
# state of the container is Starting when container is not ready but says Running
131+
self.assertTrue('running' in container['state'].keys())
132+
self.assertEqual(self.scheduler.pod.readiness_status(pod), 'Starting')
133+
134+
# verify Terminating status
135+
del container['state']['running']
136+
container['state']['terminated'] = 'fake'
137+
self.assertEqual(self.scheduler.pod.readiness_status(pod), 'Terminating')
138+
139+
# test metdata terminating
140+
del container['state']['terminated']
141+
pod['metadata']['deletionTimestamp'] = 'fake'
142+
self.assertEqual(self.scheduler.pod.readiness_status(pod), 'Terminating')
143+
del pod['metadata']['deletionTimestamp']
144+
145+
# inject fake state
146+
container['state']['random'] = 'fake'
147+
self.assertEqual(self.scheduler.pod.readiness_status(pod), 'Unknown')
148+
149+
# no containers around means Unknown
150+
pod['status']['containerStatuses'] = []
151+
self.assertEqual(self.scheduler.pod.readiness_status(pod), 'Unknown')
152+
153+
def test_ready(self):
154+
# create a pod to then manipulate
155+
name = self.create()
156+
pod = self.scheduler.pod.get(self.namespace, name).json()
157+
158+
# pod itself shouldn't be ready yet
159+
self.assertFalse(self.scheduler.pod.ready(pod))
160+
161+
# only pod but no other probe
162+
pod['status']['phase'] = 'Running'
163+
# fake out functions for failure
164+
with mock.patch('scheduler.resources.pod.Pod.readiness_status') as ready:
165+
ready.return_value = 'Starting'
166+
167+
# only readiness is ready so overall ready status is False
168+
self.assertFalse(self.scheduler.pod.ready(pod))
169+
170+
with mock.patch('scheduler.resources.pod.Pod.liveness_status') as liveness:
171+
liveness.return_value = False
172+
173+
# all things have lined up, go time
174+
self.assertFalse(self.scheduler.pod.ready(pod))
175+
176+
# fake out other functions since they are tested by themselves
177+
with mock.patch('scheduler.resources.pod.Pod.readiness_status') as ready:
178+
ready.return_value = 'Running'
179+
with mock.patch('scheduler.resources.pod.Pod.liveness_status') as liveness:
180+
# keep liveness as failing for now
181+
liveness.return_value = False
182+
183+
# only readiness is ready so overall ready status is False
184+
self.assertFalse(self.scheduler.pod.ready(pod))
185+
186+
# all things have lined up, go time
187+
liveness.return_value = True
188+
self.assertTrue(self.scheduler.pod.ready(pod))
189+
190+
def test_deleted(self):
191+
# create a pod to then manipulate
192+
name = self.create()
193+
pod = self.scheduler.pod.get(self.namespace, name).json()
194+
195+
# pod should no be deleted yet
196+
self.assertFalse(self.scheduler.pod.deleted(pod))
197+
198+
# set deleted 10 minutes in the past
199+
ts_deleted = datetime.utcnow() - timedelta(minutes=10)
200+
pod['metadata']['deletionTimestamp'] = ts_deleted.strftime(self.scheduler.DATETIME_FORMAT)
201+
self.assertTrue(self.scheduler.pod.deleted(pod))

0 commit comments

Comments
 (0)