github.com/inflatablewoman/deis@v1.0.1-0.20141111034523-a4511c46a6ce/controller/api/tests/test_hooks.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 import mock 11 import requests 12 13 from django.conf import settings 14 from django.contrib.auth.models import User 15 from django.test import TransactionTestCase 16 from rest_framework.authtoken.models import Token 17 18 19 def mock_import_repository_task(*args, **kwargs): 20 resp = requests.Response() 21 resp.status_code = 200 22 resp._content_consumed = True 23 return resp 24 25 26 class HookTest(TransactionTestCase): 27 28 """Tests API hooks used to trigger actions from external components""" 29 30 fixtures = ['tests.json'] 31 32 def setUp(self): 33 self.user = User.objects.get(username='autotest') 34 self.token = Token.objects.get(user=self.user).key 35 36 def test_push_hook(self): 37 """Test creating a Push via the API""" 38 url = '/v1/apps' 39 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 40 self.assertEqual(response.status_code, 201) 41 app_id = response.data['id'] 42 # prepare a push body 43 body = { 44 'sha': 'df1e628f2244b73f9cdf944f880a2b3470a122f4', 45 'fingerprint': '88:25:ed:67:56:91:3d:c6:1b:7f:42:c6:9b:41:24:80', 46 'receive_user': 'autotest', 47 'receive_repo': '{app_id}'.format(**locals()), 48 'ssh_connection': '10.0.1.10 50337 172.17.0.143 22', 49 'ssh_original_command': "git-receive-pack '{app_id}.git'".format(**locals()), 50 } 51 # post a request without the auth header 52 url = "/v1/hooks/push".format(**locals()) 53 response = self.client.post(url, json.dumps(body), content_type='application/json', 54 HTTP_AUTHORIZATION='token {}'.format(self.token)) 55 self.assertEqual(response.status_code, 403) 56 # now try with the builder key in the special auth header 57 response = self.client.post(url, json.dumps(body), content_type='application/json', 58 HTTP_X_DEIS_BUILDER_AUTH=settings.BUILDER_KEY) 59 self.assertEqual(response.status_code, 201) 60 for k in ('owner', 'app', 'sha', 'fingerprint', 'receive_repo', 'receive_user', 61 'ssh_connection', 'ssh_original_command'): 62 self.assertIn(k, response.data) 63 64 def test_push_abuse(self): 65 """Test a user pushing to an unauthorized application""" 66 # create a legit app as "autotest" 67 url = '/v1/apps' 68 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 69 self.assertEqual(response.status_code, 201) 70 app_id = response.data['id'] 71 # register an evil user 72 username, password = 'eviluser', 'password' 73 first_name, last_name = 'Evil', 'User' 74 email = 'evil@deis.io' 75 submit = { 76 'username': username, 77 'password': password, 78 'first_name': first_name, 79 'last_name': last_name, 80 'email': email, 81 } 82 url = '/v1/auth/register' 83 response = self.client.post(url, json.dumps(submit), content_type='application/json') 84 self.assertEqual(response.status_code, 201) 85 # prepare a push body that simulates a git push 86 body = { 87 'sha': 'df1e628f2244b73f9cdf944f880a2b3470a122f4', 88 'fingerprint': '88:25:ed:67:56:91:3d:c6:1b:7f:42:c6:9b:41:24:99', 89 'receive_user': 'eviluser', 90 'receive_repo': '{app_id}'.format(**locals()), 91 'ssh_connection': '10.0.1.10 50337 172.17.0.143 22', 92 'ssh_original_command': "git-receive-pack '{app_id}.git'".format(**locals()), 93 } 94 # try to push as "eviluser" 95 url = "/v1/hooks/push".format(**locals()) 96 response = self.client.post(url, json.dumps(body), content_type='application/json', 97 HTTP_X_DEIS_BUILDER_AUTH=settings.BUILDER_KEY) 98 self.assertEqual(response.status_code, 403) 99 100 @mock.patch('requests.post', mock_import_repository_task) 101 def test_build_hook(self): 102 """Test creating a Build via an API Hook""" 103 url = '/v1/apps' 104 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 105 self.assertEqual(response.status_code, 201) 106 app_id = response.data['id'] 107 build = {'username': 'autotest', 'app': app_id} 108 url = '/v1/hooks/builds'.format(**locals()) 109 body = {'receive_user': 'autotest', 110 'receive_repo': app_id, 111 'image': '{app_id}:v2'.format(**locals())} 112 # post the build without an auth token 113 response = self.client.post(url, json.dumps(body), content_type='application/json') 114 self.assertEqual(response.status_code, 401) 115 # post the build with the builder auth key 116 response = self.client.post(url, json.dumps(body), content_type='application/json', 117 HTTP_X_DEIS_BUILDER_AUTH=settings.BUILDER_KEY) 118 self.assertEqual(response.status_code, 200) 119 self.assertIn('release', response.data) 120 self.assertIn('version', response.data['release']) 121 self.assertIn('domains', response.data) 122 123 def test_build_hook_procfile(self): 124 """Test creating a Procfile build via an API Hook""" 125 url = '/v1/apps' 126 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 127 self.assertEqual(response.status_code, 201) 128 app_id = response.data['id'] 129 build = {'username': 'autotest', 'app': app_id} 130 url = '/v1/hooks/builds'.format(**locals()) 131 PROCFILE = {'web': 'node server.js', 'worker': 'node worker.js'} 132 SHA = 'ecdff91c57a0b9ab82e89634df87e293d259a3aa' 133 body = {'receive_user': 'autotest', 134 'receive_repo': app_id, 135 'image': '{app_id}:v2'.format(**locals()), 136 'sha': SHA, 137 'procfile': PROCFILE} 138 # post the build without an auth token 139 response = self.client.post(url, json.dumps(body), content_type='application/json') 140 self.assertEqual(response.status_code, 401) 141 # post the build with the builder auth key 142 response = self.client.post(url, json.dumps(body), content_type='application/json', 143 HTTP_X_DEIS_BUILDER_AUTH=settings.BUILDER_KEY) 144 self.assertEqual(response.status_code, 200) 145 self.assertIn('release', response.data) 146 self.assertIn('version', response.data['release']) 147 self.assertIn('domains', response.data) 148 # make sure build fields were populated 149 url = '/v1/apps/{app_id}/builds'.format(**locals()) 150 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 151 self.assertEqual(response.status_code, 200) 152 self.assertIn('results', response.data) 153 build = response.data['results'][0] 154 self.assertEqual(build['sha'], SHA) 155 self.assertEqual(build['procfile'], PROCFILE) 156 # test listing/retrieving container info 157 url = "/v1/apps/{app_id}/containers/web".format(**locals()) 158 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 159 self.assertEqual(response.status_code, 200) 160 self.assertEqual(len(response.data['results']), 1) 161 container = response.data['results'][0] 162 self.assertEqual(container['type'], 'web') 163 self.assertEqual(container['num'], 1) 164 165 def test_build_hook_dockerfile(self): 166 """Test creating a Dockerfile build via an API Hook""" 167 url = '/v1/apps' 168 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 169 self.assertEqual(response.status_code, 201) 170 app_id = response.data['id'] 171 build = {'username': 'autotest', 'app': app_id} 172 url = '/v1/hooks/builds'.format(**locals()) 173 SHA = 'ecdff91c57a0b9ab82e89634df87e293d259a3aa' 174 DOCKERFILE = """ 175 FROM busybox 176 CMD /bin/true 177 """ 178 body = {'receive_user': 'autotest', 179 'receive_repo': app_id, 180 'image': '{app_id}:v2'.format(**locals()), 181 'sha': SHA, 182 'dockerfile': DOCKERFILE} 183 # post the build without an auth token 184 response = self.client.post(url, json.dumps(body), content_type='application/json') 185 self.assertEqual(response.status_code, 401) 186 # post the build with the builder auth key 187 response = self.client.post(url, json.dumps(body), content_type='application/json', 188 HTTP_X_DEIS_BUILDER_AUTH=settings.BUILDER_KEY) 189 self.assertEqual(response.status_code, 200) 190 self.assertIn('release', response.data) 191 self.assertIn('version', response.data['release']) 192 self.assertIn('domains', response.data) 193 # make sure build fields were populated 194 url = '/v1/apps/{app_id}/builds'.format(**locals()) 195 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 196 self.assertEqual(response.status_code, 200) 197 self.assertIn('results', response.data) 198 build = response.data['results'][0] 199 self.assertEqual(build['sha'], SHA) 200 self.assertEqual(build['dockerfile'], DOCKERFILE) 201 # test default container 202 url = "/v1/apps/{app_id}/containers/cmd".format(**locals()) 203 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 204 self.assertEqual(response.status_code, 200) 205 self.assertEqual(len(response.data['results']), 1) 206 container = response.data['results'][0] 207 self.assertEqual(container['type'], 'cmd') 208 self.assertEqual(container['num'], 1) 209 210 def test_config_hook(self): 211 """Test reading Config via an API Hook""" 212 url = '/v1/apps' 213 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 214 self.assertEqual(response.status_code, 201) 215 app_id = response.data['id'] 216 url = '/v1/apps/{app_id}/config'.format(**locals()) 217 response = self.client.get(url, HTTP_AUTHORIZATION='token {}'.format(self.token)) 218 self.assertEqual(response.status_code, 200) 219 self.assertIn('values', response.data) 220 values = response.data['values'] 221 # prepare the config hook 222 config = {'username': 'autotest', 'app': app_id} 223 url = '/v1/hooks/config'.format(**locals()) 224 body = {'receive_user': 'autotest', 225 'receive_repo': app_id} 226 # post without an auth token 227 response = self.client.post(url, json.dumps(body), content_type='application/json') 228 self.assertEqual(response.status_code, 401) 229 # post with the builder auth key 230 response = self.client.post(url, json.dumps(body), content_type='application/json', 231 HTTP_X_DEIS_BUILDER_AUTH=settings.BUILDER_KEY) 232 self.assertEqual(response.status_code, 200) 233 self.assertIn('values', response.data) 234 self.assertEqual(values, response.data['values']) 235 236 def test_admin_can_hook(self): 237 """Administrator should be able to create build hooks on non-admin apps. 238 """ 239 """Test creating a Push via the API""" 240 user = User.objects.get(username='autotest2') 241 token = Token.objects.get(user=user).key 242 url = '/v1/apps' 243 response = self.client.post(url, HTTP_AUTHORIZATION='token {}'.format(token)) 244 self.assertEqual(response.status_code, 201) 245 app_id = response.data['id'] 246 # prepare a push body 247 DOCKERFILE = """ 248 FROM busybox 249 CMD /bin/true 250 """ 251 body = {'receive_user': 'autotest', 252 'receive_repo': app_id, 253 'image': '{app_id}:v2'.format(**locals()), 254 'sha': 'ecdff91c57a0b9ab82e89634df87e293d259a3aa', 255 'dockerfile': DOCKERFILE} 256 url = '/v1/hooks/builds' 257 response = self.client.post(url, json.dumps(body), content_type='application/json', 258 HTTP_X_DEIS_BUILDER_AUTH=settings.BUILDER_KEY) 259 self.assertEqual(response.status_code, 200) 260 self.assertEqual(response.data['release']['version'], 2)