Skip to content

Commit aaccdf7

Browse files
authored
chore(tests): improve test coverage for HPA (#1081)
ref #60
1 parent f5d1454 commit aaccdf7

2 files changed

Lines changed: 253 additions & 20 deletions

File tree

rootfs/scheduler/tests/test_horizontalpodautoscaler.py

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
Run the tests with './manage.py test scheduler'
55
"""
6-
from scheduler import KubeHTTPException
6+
from scheduler import KubeHTTPException, KubeException
77
from scheduler.tests import TestCase
88
from scheduler.utils import generate_random_name
99

@@ -19,26 +19,28 @@ def create(self, namespace=None, name=generate_random_name(), **kwargs):
1919
"""
2020
namespace = self.namespace if namespace is None else namespace
2121
# these are all required even if it is kwargs...
22-
kwargs = {
22+
d_kwargs = {
2323
'app_type': kwargs.get('app_type', 'web'),
2424
'version': kwargs.get('version', 'v99'),
2525
'replicas': kwargs.get('replicas', 1),
2626
'pod_termination_grace_period_seconds': 2,
27+
'image': 'quay.io/fake/image',
28+
'entrypoint': 'sh',
29+
'command': 'start',
2730
}
2831

2932
# create a Deployment to test HPA with
30-
deployment = self.scheduler.deployment.create(namespace, name, 'quay.io/fake/image',
31-
'sh', 'start', **kwargs)
33+
deployment = self.scheduler.deployment.create(namespace, name, **d_kwargs)
3234
self.assertEqual(deployment.status_code, 201, deployment.json())
3335

3436
# create HPA referencing the Deployment above
35-
kwargs = {
36-
'min': 2,
37-
'max': 4,
37+
data = {
38+
'min': kwargs.get('min', 2),
39+
'max': kwargs.get('max', 4),
3840
'cpu_percent': 45,
3941
'wait': True
4042
}
41-
horizontalpodautoscaler = self.scheduler.hpa.create(namespace, name, kwargs.get('app_type'), deployment.json(), **kwargs) # noqa
43+
horizontalpodautoscaler = self.scheduler.hpa.create(namespace, name, kwargs.get('app_type'), deployment.json(), **data) # noqa
4244
self.assertEqual(horizontalpodautoscaler.status_code, 201, horizontalpodautoscaler.json()) # noqa
4345
return name
4446

@@ -49,13 +51,13 @@ def update(self, namespace=None, name=generate_random_name(), **kwargs):
4951
namespace = self.namespace if namespace is None else namespace
5052
deployment = self.scheduler.deployment.get(namespace, name)
5153

52-
kwargs = {
53-
'min': kwargs.get('replicas'),
54-
'max': 4,
54+
data = {
55+
'min': kwargs.get('min', 2),
56+
'max': kwargs.get('max', 4),
5557
'cpu_percent': 45,
5658
'wait': True
5759
}
58-
horizontalpodautoscaler = self.scheduler.hpa.update(namespace, name, kwargs.get('app_type'), deployment.json(), **kwargs) # noqa
60+
horizontalpodautoscaler = self.scheduler.hpa.update(namespace, name, kwargs.get('app_type'), deployment.json(), **data) # noqa
5961
self.assertEqual(horizontalpodautoscaler.status_code, 200, horizontalpodautoscaler.json()) # noqa
6062
return name
6163

@@ -65,15 +67,17 @@ def update_deployment(self, namespace=None, name=generate_random_name(), **kwarg
6567
"""
6668
namespace = self.namespace if namespace is None else namespace
6769
# these are all required even if it is kwargs...
68-
kwargs = {
70+
d_kwargs = {
6971
'app_type': kwargs.get('app_type', 'web'),
7072
'version': kwargs.get('version', 'v99'),
7173
'replicas': kwargs.get('replicas', 4),
7274
'pod_termination_grace_period_seconds': 2,
75+
'image': 'quay.io/fake/image',
76+
'entrypoint': 'sh',
77+
'command': 'start',
7378
}
7479

75-
deployment = self.scheduler.deployment.update(namespace, name, 'quay.io/fake/image',
76-
'sh', 'start', **kwargs)
80+
deployment = self.scheduler.deployment.update(namespace, name, **d_kwargs)
7781
data = deployment.json()
7882
self.assertEqual(deployment.status_code, 200, data)
7983
return name
@@ -85,6 +89,18 @@ def test_create_failure(self):
8589
):
8690
self.create('doesnotexist', 'doesnotexist')
8791

92+
with self.assertRaises(
93+
KubeException,
94+
msg='min replicas needs to be 1 or higher'
95+
):
96+
self.create(self.namespace, 'doesnotexist', min=0)
97+
98+
with self.assertRaises(
99+
KubeException,
100+
msg='max replicas can not be smaller than min replicas'
101+
):
102+
self.create(self.namespace, 'doesnotexist', min=2, max=1)
103+
88104
def test_create(self):
89105
name = self.create()
90106

@@ -118,7 +134,7 @@ def test_update(self):
118134
self.assertEqual(deployment['status']['availableReplicas'], 2)
119135

120136
# update HPA to 3 replicas minimum
121-
self.update(self.namespace, name, replicas=3)
137+
self.update(self.namespace, name, min=3)
122138

123139
# check the deployment object
124140
deployment = self.scheduler.deployment.get(self.namespace, name).json()
@@ -190,10 +206,7 @@ def test_get_horizontalpodautoscaler(self):
190206
response = self.scheduler.hpa.get(self.namespace, name)
191207
data = response.json()
192208
self.assertEqual(response.status_code, 200, data)
193-
if self.scheduler.version() < 1.3:
194-
self.assertEqual(data['apiVersion'], 'extensions/v1beta1')
195-
else:
196-
self.assertEqual(data['apiVersion'], 'autoscaling/v1')
209+
self.assertEqual(data['apiVersion'], 'autoscaling/v1')
197210
self.assertEqual(data['kind'], 'HorizontalPodAutoscaler')
198211
self.assertEqual(data['metadata']['name'], name)
199212
self.assertDictContainsSubset(
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
"""
2+
Unit tests for the Deis scheduler module.
3+
4+
Run the tests with './manage.py test scheduler'
5+
"""
6+
from unittest import mock
7+
from scheduler import KubeHTTPException, KubeException
8+
from scheduler.tests import TestCase
9+
from scheduler.utils import generate_random_name
10+
11+
12+
@mock.patch('scheduler.KubeHTTPClient.version', lambda *args: 1.2)
13+
class HorizontalPodAutoscalersTest(TestCase):
14+
"""Tests scheduler horizontalpodautoscaler calls"""
15+
16+
def create(self, namespace=None, name=generate_random_name(), **kwargs):
17+
"""
18+
Helper function to create and verify a horizontalpodautoscaler on the namespace
19+
20+
Creates a Deployment so that HPA can work off an object
21+
"""
22+
namespace = self.namespace if namespace is None else namespace
23+
# these are all required even if it is kwargs...
24+
d_kwargs = {
25+
'app_type': kwargs.get('app_type', 'web'),
26+
'version': kwargs.get('version', 'v99'),
27+
'replicas': kwargs.get('replicas', 1),
28+
'pod_termination_grace_period_seconds': 2,
29+
'image': 'quay.io/fake/image',
30+
'entrypoint': 'sh',
31+
'command': 'start',
32+
}
33+
34+
# create a Deployment to test HPA with
35+
deployment = self.scheduler.deployment.create(namespace, name, **d_kwargs)
36+
self.assertEqual(deployment.status_code, 201, deployment.json())
37+
38+
# create HPA referencing the Deployment above
39+
data = {
40+
'min': kwargs.get('min', 2),
41+
'max': kwargs.get('max', 4),
42+
'cpu_percent': 45,
43+
'wait': True
44+
}
45+
horizontalpodautoscaler = self.scheduler.hpa.create(namespace, name, d_kwargs.get('app_type'), deployment.json(), **data) # noqa
46+
self.assertEqual(horizontalpodautoscaler.status_code, 201, horizontalpodautoscaler.json()) # noqa
47+
return name
48+
49+
def update(self, namespace=None, name=generate_random_name(), **kwargs):
50+
"""
51+
Helper function to update and verify a horizontalpodautoscaler on the namespace
52+
"""
53+
namespace = self.namespace if namespace is None else namespace
54+
deployment = self.scheduler.deployment.get(namespace, name)
55+
56+
data = {
57+
'min': kwargs.get('min', 2),
58+
'max': kwargs.get('max', 4),
59+
'cpu_percent': 45,
60+
'wait': True
61+
}
62+
horizontalpodautoscaler = self.scheduler.hpa.update(namespace, name, kwargs.get('app_type'), deployment.json(), **data) # noqa
63+
self.assertEqual(horizontalpodautoscaler.status_code, 200, horizontalpodautoscaler.json()) # noqa
64+
return name
65+
66+
def update_deployment(self, namespace=None, name=generate_random_name(), **kwargs):
67+
"""
68+
Helper function to update and verify a deployment on the namespace
69+
"""
70+
namespace = self.namespace if namespace is None else namespace
71+
# these are all required even if it is kwargs...
72+
kwargs = {
73+
'app_type': kwargs.get('app_type', 'web'),
74+
'version': kwargs.get('version', 'v99'),
75+
'replicas': kwargs.get('replicas', 4),
76+
'pod_termination_grace_period_seconds': 2,
77+
'image': 'quay.io/fake/image',
78+
'entrypoint': 'sh',
79+
'command': 'start',
80+
}
81+
82+
deployment = self.scheduler.deployment.update(namespace, name, **kwargs)
83+
data = deployment.json()
84+
self.assertEqual(deployment.status_code, 200, data)
85+
return name
86+
87+
def test_create_failure(self):
88+
with self.assertRaises(
89+
KubeHTTPException,
90+
msg='failed to create HorizontalPodAutoscaler doesnotexist in Namespace {}: 404 Not Found'.format(self.namespace) # noqa
91+
):
92+
self.create('doesnotexist', 'doesnotexist')
93+
94+
with self.assertRaises(
95+
KubeException,
96+
msg='min replicas needs to be 1 or higher'
97+
):
98+
self.create(self.namespace, 'doesnotexist', min=0)
99+
100+
with self.assertRaises(
101+
KubeException,
102+
msg='max replicas can not be smaller than min replicas'
103+
):
104+
self.create(self.namespace, 'doesnotexist', min=2, max=1)
105+
106+
def test_create(self):
107+
name = self.create()
108+
109+
# check the deployment object
110+
deployment = self.scheduler.deployment.get(self.namespace, name).json()
111+
self.assertEqual(deployment['spec']['replicas'], 2, deployment)
112+
113+
# make sure HPA kicked things from 1 (set by Deployments) to 2 (HPA min)
114+
labels = {'app': self.namespace, 'type': 'web', 'version': 'v99'}
115+
pods = self.scheduler.pod.get(self.namespace, labels=labels).json()
116+
self.assertEqual(len(pods['items']), 2)
117+
118+
def test_update_horizontalpodautoscaler_failure(self):
119+
# test failure
120+
with self.assertRaises(
121+
KubeHTTPException,
122+
msg='failed to update HorizontalPodAutoscaler foo in Namespace {}: 404 Not Found'.format(self.namespace) # noqa
123+
):
124+
self.update(self.namespace, 'foo')
125+
126+
def test_update(self):
127+
# test success
128+
name = self.create()
129+
130+
# check the deployment object
131+
deployment = self.scheduler.deployment.get(self.namespace, name).json()
132+
self.assertEqual(deployment['spec']['replicas'], 2, deployment)
133+
134+
# make sure HPA kicked things from 1 (set by Deployments) to 2 (HPA min)
135+
deployment = self.scheduler.deployment.get(self.namespace, name).json()
136+
self.assertEqual(deployment['status']['availableReplicas'], 2)
137+
138+
# update HPA to 3 replicas minimum
139+
self.update(self.namespace, name, min=3)
140+
141+
# check the deployment object
142+
deployment = self.scheduler.deployment.get(self.namespace, name).json()
143+
self.assertEqual(deployment['spec']['replicas'], 3, deployment)
144+
145+
# make sure HPA kicked things from 1 (set by Deployments) to 3 (HPA min)
146+
deployment = self.scheduler.deployment.get(self.namespace, name).json()
147+
self.assertEqual(deployment['status']['availableReplicas'], 3)
148+
149+
# scale deployment to 1 (should go back to 3)
150+
self.update_deployment(self.namespace, name, replicas=1)
151+
152+
# check the deployment object
153+
deployment = self.scheduler.deployment.get(self.namespace, name).json()
154+
self.assertEqual(deployment['spec']['replicas'], 3, deployment)
155+
156+
# make sure HPA kicked things from 1 (set by Deployments) to 3 (HPA min)
157+
deployment = self.scheduler.deployment.get(self.namespace, name).json()
158+
self.assertEqual(deployment['status']['availableReplicas'], 3)
159+
160+
# scale deployment to 6 (should go back to 4)
161+
self.update_deployment(self.namespace, name, replicas=6)
162+
163+
# check the deployment object
164+
deployment = self.scheduler.deployment.get(self.namespace, name).json()
165+
self.assertEqual(deployment['spec']['replicas'], 4, deployment)
166+
167+
# make sure HPA kicked things from 6 (set by Deployments) to 4 (HPA min)
168+
deployment = self.scheduler.deployment.get(self.namespace, name).json()
169+
self.assertEqual(deployment['status']['availableReplicas'], 4)
170+
171+
def test_delete_failure(self):
172+
# test failure
173+
with self.assertRaises(
174+
KubeHTTPException,
175+
msg='failed to delete HorizontalPodAutoscaler foo in Namespace {}: 404 Not Found'.format(self.namespace) # noqa
176+
):
177+
self.scheduler.hpa.delete(self.namespace, 'foo')
178+
179+
def test_delete(self):
180+
# test success
181+
name = self.create()
182+
response = self.scheduler.hpa.delete(self.namespace, name)
183+
data = response.json()
184+
self.assertEqual(response.status_code, 200, data)
185+
186+
def test_get_horizontalpodautoscalers(self):
187+
# test success
188+
name = self.create()
189+
response = self.scheduler.hpa.get(self.namespace)
190+
data = response.json()
191+
self.assertEqual(response.status_code, 200, data)
192+
self.assertIn('items', data)
193+
self.assertEqual(1, len(data['items']), data['items'])
194+
# simple verify of data
195+
self.assertEqual(data['items'][0]['metadata']['name'], name, data)
196+
197+
def test_get_horizontalpodautoscaler_failure(self):
198+
# test failure
199+
with self.assertRaises(
200+
KubeHTTPException,
201+
msg='failed to get HorizontalPodAutoscaler doesnotexist in Namespace {}: 404 Not Found'.format(self.namespace) # noqa
202+
):
203+
self.scheduler.hpa.get(self.namespace, 'doesnotexist')
204+
205+
def test_get_horizontalpodautoscaler(self):
206+
# test success
207+
name = self.create()
208+
response = self.scheduler.hpa.get(self.namespace, name)
209+
data = response.json()
210+
self.assertEqual(response.status_code, 200, data)
211+
self.assertEqual(data['apiVersion'], 'extensions/v1beta1')
212+
self.assertEqual(data['kind'], 'HorizontalPodAutoscaler')
213+
self.assertEqual(data['metadata']['name'], name)
214+
self.assertDictContainsSubset(
215+
{
216+
'app': self.namespace,
217+
'heritage': 'deis'
218+
},
219+
data['metadata']['labels']
220+
)

0 commit comments

Comments
 (0)