github.com/misfo/deis@v1.0.1-0.20141111224634-e0eee0392b8a/controller/api/tests/test_scheduler.py (about) 1 """ 2 Unit tests for the Deis api app. 3 4 Run the tests with "./manage.py test api" 5 """ 6 7 from __future__ import unicode_literals 8 9 import json 10 11 from django.conf import settings 12 from django.contrib.auth.models import User 13 from django.test import TransactionTestCase 14 from rest_framework.authtoken.models import Token 15 16 from scheduler import chaos 17 18 19 class SchedulerTest(TransactionTestCase): 20 """Tests creation of containers on nodes""" 21 22 fixtures = ['tests.json'] 23 24 def setUp(self): 25 self.user = User.objects.get(username='autotest') 26 self.token = Token.objects.get(user=self.user).key 27 # start without any chaos 28 chaos.CREATE_ERROR_RATE = 0 29 chaos.DESTROY_ERROR_RATE = 0 30 chaos.START_ERROR_RATE = 0 31 chaos.STOP_ERROR_RATE = 0 32 # use chaos scheduler 33 settings.SCHEDULER_MODULE = 'chaos' 34 # provide mock authentication used for run commands 35 settings.SSH_PRIVATE_KEY = '<some-ssh-private-key>' 36 37 def tearDown(self): 38 # reset for subsequent tests 39 settings.SCHEDULER_MODULE = 'mock' 40 settings.SSH_PRIVATE_KEY = '' 41 42 def test_create_chaos(self): 43 url = '/v1/apps' 44 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 45 self.assertEqual(response.status_code, 201) 46 app_id = response.data['id'] 47 # post a new build 48 url = "/v1/apps/{app_id}/builds".format(**locals()) 49 body = {'image': 'autotest/example', 'sha': 'a'*40, 50 'procfile': json.dumps({'web': 'node server.js', 'worker': 'node worker.js'})} 51 response = self.client.post(url, json.dumps(body), content_type='application/json', 52 HTTP_AUTHORIZATION='token {}'.format(self.token)) 53 self.assertEqual(response.status_code, 201) 54 url = "/v1/apps/{app_id}/containers".format(**locals()) 55 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 56 self.assertEqual(response.status_code, 200) 57 self.assertEqual(len(response.data['results']), 1) 58 # scale to zero for consistency 59 url = "/v1/apps/{app_id}/scale".format(**locals()) 60 body = {'web': 0} 61 response = self.client.post(url, json.dumps(body), content_type='application/json', 62 HTTP_AUTHORIZATION='token {}'.format(self.token)) 63 self.assertEqual(response.status_code, 204) 64 # let's get chaotic 65 chaos.CREATE_ERROR_RATE = 0.5 66 # scale up but expect a 503 67 url = "/v1/apps/{app_id}/scale".format(**locals()) 68 body = {'web': 20} 69 response = self.client.post(url, json.dumps(body), content_type='application/json', 70 HTTP_AUTHORIZATION='token {}'.format(self.token)) 71 self.assertEqual(response.status_code, 503) 72 # inspect broken containers 73 url = "/v1/apps/{app_id}/containers".format(**locals()) 74 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 75 self.assertEqual(response.status_code, 200) 76 self.assertEqual(len(response.data['results']), 20) 77 # make sure some failed 78 states = set([c['state'] for c in response.data['results']]) 79 self.assertEqual(states, set(['error', 'created'])) 80 81 def test_start_chaos(self): 82 url = '/v1/apps' 83 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 84 self.assertEqual(response.status_code, 201) 85 app_id = response.data['id'] 86 # post a new build 87 url = "/v1/apps/{app_id}/builds".format(**locals()) 88 body = {'image': 'autotest/example', 'sha': 'a'*40, 89 'procfile': json.dumps({'web': 'node server.js', 'worker': 'node worker.js'})} 90 response = self.client.post(url, json.dumps(body), content_type='application/json', 91 HTTP_AUTHORIZATION='token {}'.format(self.token)) 92 self.assertEqual(response.status_code, 201) 93 url = "/v1/apps/{app_id}/containers".format(**locals()) 94 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 95 self.assertEqual(response.status_code, 200) 96 self.assertEqual(len(response.data['results']), 1) 97 # scale to zero for consistency 98 url = "/v1/apps/{app_id}/scale".format(**locals()) 99 body = {'web': 0} 100 response = self.client.post(url, json.dumps(body), content_type='application/json', 101 HTTP_AUTHORIZATION='token {}'.format(self.token)) 102 self.assertEqual(response.status_code, 204) 103 # let's get chaotic 104 chaos.START_ERROR_RATE = 0.5 105 # scale up, which will allow some crashed containers 106 url = "/v1/apps/{app_id}/scale".format(**locals()) 107 body = {'web': 20} 108 response = self.client.post(url, json.dumps(body), content_type='application/json', 109 HTTP_AUTHORIZATION='token {}'.format(self.token)) 110 self.assertEqual(response.status_code, 204) 111 # inspect broken containers 112 url = "/v1/apps/{app_id}/containers".format(**locals()) 113 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 114 self.assertEqual(response.status_code, 200) 115 self.assertEqual(len(response.data['results']), 20) 116 # make sure some failed 117 states = set([c['state'] for c in response.data['results']]) 118 self.assertEqual(states, set(['crashed', 'up'])) 119 120 def test_destroy_chaos(self): 121 url = '/v1/apps' 122 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 123 self.assertEqual(response.status_code, 201) 124 app_id = response.data['id'] 125 # post a new build 126 url = "/v1/apps/{app_id}/builds".format(**locals()) 127 body = {'image': 'autotest/example', 'sha': 'a'*40, 128 'procfile': json.dumps({'web': 'node server.js', 'worker': 'node worker.js'})} 129 response = self.client.post(url, json.dumps(body), content_type='application/json', 130 HTTP_AUTHORIZATION='token {}'.format(self.token)) 131 self.assertEqual(response.status_code, 201) 132 url = "/v1/apps/{app_id}/containers".format(**locals()) 133 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 134 self.assertEqual(response.status_code, 200) 135 self.assertEqual(len(response.data['results']), 1) 136 # scale up 137 url = "/v1/apps/{app_id}/scale".format(**locals()) 138 body = {'web': 20} 139 response = self.client.post(url, json.dumps(body), content_type='application/json', 140 HTTP_AUTHORIZATION='token {}'.format(self.token)) 141 self.assertEqual(response.status_code, 204) 142 url = "/v1/apps/{app_id}/containers".format(**locals()) 143 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 144 self.assertEqual(response.status_code, 200) 145 self.assertEqual(len(response.data['results']), 20) 146 # let's get chaotic 147 chaos.DESTROY_ERROR_RATE = 0.5 148 # scale to zero but expect a 503 149 url = "/v1/apps/{app_id}/scale".format(**locals()) 150 body = {'web': 0} 151 response = self.client.post(url, json.dumps(body), content_type='application/json', 152 HTTP_AUTHORIZATION='token {}'.format(self.token)) 153 self.assertEqual(response.status_code, 503) 154 # inspect broken containers 155 url = "/v1/apps/{app_id}/containers".format(**locals()) 156 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 157 self.assertEqual(response.status_code, 200) 158 states = set([c['state'] for c in response.data['results']]) 159 self.assertEqual(states, set(['error'])) 160 # make sure we can cleanup after enough tries 161 containers = 20 162 for _ in range(100): 163 url = "/v1/apps/{app_id}/scale".format(**locals()) 164 body = {'web': 0} 165 response = self.client.post(url, json.dumps(body), content_type='application/json', 166 HTTP_AUTHORIZATION='token {}'.format(self.token)) 167 # break if we destroyed successfully 168 if response.status_code == 204: 169 break 170 self.assertEquals(response.status_code, 503) 171 # inspect broken containers 172 url = "/v1/apps/{app_id}/containers".format(**locals()) 173 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 174 self.assertEqual(response.status_code, 200) 175 containers = len(response.data['results']) 176 177 def test_build_chaos(self): 178 url = '/v1/apps' 179 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 180 self.assertEqual(response.status_code, 201) 181 app_id = response.data['id'] 182 # post a new build 183 url = "/v1/apps/{app_id}/builds".format(**locals()) 184 body = {'image': 'autotest/example', 'sha': 'a'*40, 185 'procfile': json.dumps({'web': 'node server.js', 'worker': 'node worker.js'})} 186 response = self.client.post(url, json.dumps(body), content_type='application/json', 187 HTTP_AUTHORIZATION='token {}'.format(self.token)) 188 self.assertEqual(response.status_code, 201) 189 # inspect builds 190 url = "/v1/apps/{app_id}/builds".format(**locals()) 191 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 192 self.assertEqual(response.status_code, 200) 193 self.assertEqual(len(response.data['results']), 1) 194 # inspect releases 195 url = "/v1/apps/{app_id}/releases".format(**locals()) 196 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 197 self.assertEqual(response.status_code, 200) 198 self.assertEqual(len(response.data['results']), 2) 199 url = "/v1/apps/{app_id}/containers".format(**locals()) 200 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 201 self.assertEqual(response.status_code, 200) 202 self.assertEqual(len(response.data['results']), 1) 203 # scale up 204 url = "/v1/apps/{app_id}/scale".format(**locals()) 205 body = {'web': 20} 206 response = self.client.post(url, json.dumps(body), content_type='application/json', 207 HTTP_AUTHORIZATION='token {}'.format(self.token)) 208 self.assertEqual(response.status_code, 204) 209 # simulate failing to create containers 210 chaos.CREATE_ERROR_RATE = 0.5 211 chaos.START_ERROR_RATE = 0.5 212 # post a new build 213 url = "/v1/apps/{app_id}/builds".format(**locals()) 214 body = {'image': 'autotest/example', 'sha': 'b'*40, 215 'procfile': json.dumps({'web': 'node server.js', 'worker': 'node worker.js'})} 216 response = self.client.post(url, json.dumps(body), content_type='application/json', 217 HTTP_AUTHORIZATION='token {}'.format(self.token)) 218 self.assertEqual(response.status_code, 503) 219 # inspect releases 220 url = "/v1/apps/{app_id}/releases".format(**locals()) 221 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 222 self.assertEqual(response.status_code, 200) 223 self.assertEqual(len(response.data['results']), 2) 224 # inspect containers 225 url = "/v1/apps/{app_id}/containers".format(**locals()) 226 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 227 self.assertEqual(response.status_code, 200) 228 self.assertEqual(len(response.data['results']), 20) 229 230 # make sure all old containers are still up 231 states = set([c['state'] for c in response.data['results']]) 232 self.assertEqual(states, set(['up'])) 233 234 def test_config_chaos(self): 235 url = '/v1/apps' 236 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 237 self.assertEqual(response.status_code, 201) 238 app_id = response.data['id'] 239 # post a new build 240 url = "/v1/apps/{app_id}/builds".format(**locals()) 241 body = {'image': 'autotest/example', 'sha': 'a'*40, 242 'procfile': json.dumps({'web': 'node server.js', 'worker': 'node worker.js'})} 243 response = self.client.post(url, json.dumps(body), content_type='application/json', 244 HTTP_AUTHORIZATION='token {}'.format(self.token)) 245 self.assertEqual(response.status_code, 201) 246 # inspect releases 247 url = "/v1/apps/{app_id}/releases".format(**locals()) 248 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 249 self.assertEqual(response.status_code, 200) 250 self.assertEqual(len(response.data['results']), 2) 251 url = "/v1/apps/{app_id}/containers".format(**locals()) 252 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 253 self.assertEqual(response.status_code, 200) 254 self.assertEqual(len(response.data['results']), 1) 255 # scale up 256 url = "/v1/apps/{app_id}/scale".format(**locals()) 257 body = {'web': 20} 258 response = self.client.post(url, json.dumps(body), content_type='application/json', 259 HTTP_AUTHORIZATION='token {}'.format(self.token)) 260 self.assertEqual(response.status_code, 204) 261 # simulate failing to create or start containers 262 chaos.CREATE_ERROR_RATE = 0.5 263 chaos.START_ERROR_RATE = 0.5 264 # post a new config 265 url = "/v1/apps/{app_id}/config".format(**locals()) 266 body = {'values': json.dumps({'NEW_URL1': 'http://localhost:8080/'})} 267 response = self.client.post(url, json.dumps(body), content_type='application/json', 268 HTTP_AUTHORIZATION='token {}'.format(self.token)) 269 self.assertEqual(response.status_code, 503) 270 # inspect releases 271 url = "/v1/apps/{app_id}/releases".format(**locals()) 272 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 273 self.assertEqual(response.status_code, 200) 274 self.assertEqual(len(response.data['results']), 2) 275 # inspect containers 276 url = "/v1/apps/{app_id}/containers".format(**locals()) 277 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 278 self.assertEqual(response.status_code, 200) 279 self.assertEqual(len(response.data['results']), 20) 280 # make sure all old containers are still up 281 states = set([c['state'] for c in response.data['results']]) 282 self.assertEqual(states, set(['up'])) 283 284 def test_run_chaos(self): 285 url = '/v1/apps' 286 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 287 self.assertEqual(response.status_code, 201) 288 app_id = response.data['id'] 289 # post a new build 290 url = "/v1/apps/{app_id}/builds".format(**locals()) 291 body = {'image': 'autotest/example', 'sha': 'a'*40, 292 'procfile': json.dumps({'web': 'node server.js', 'worker': 'node worker.js'})} 293 response = self.client.post(url, json.dumps(body), content_type='application/json', 294 HTTP_AUTHORIZATION='token {}'.format(self.token)) 295 self.assertEqual(response.status_code, 201) 296 # inspect builds 297 url = "/v1/apps/{app_id}/builds".format(**locals()) 298 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 299 self.assertEqual(response.status_code, 200) 300 self.assertEqual(len(response.data['results']), 1) 301 # inspect releases 302 url = "/v1/apps/{app_id}/releases".format(**locals()) 303 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 304 self.assertEqual(response.status_code, 200) 305 self.assertEqual(len(response.data['results']), 2) 306 url = "/v1/apps/{app_id}/containers".format(**locals()) 307 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 308 self.assertEqual(response.status_code, 200) 309 self.assertEqual(len(response.data['results']), 1) 310 # block all create operations 311 chaos.CREATE_ERROR_RATE = 1 312 # make sure the run fails with a 503 313 url = '/v1/apps/{app_id}/run'.format(**locals()) 314 body = {'command': 'ls -al'} 315 response = self.client.post(url, json.dumps(body), content_type='application/json', 316 HTTP_AUTHORIZATION='token {}'.format(self.token)) 317 self.assertEqual(response.status_code, 503)