diff --git a/tatu/api/app.py b/tatu/api/app.py index 4d48861..5fb483d 100644 --- a/tatu/api/app.py +++ b/tatu/api/app.py @@ -10,7 +10,8 @@ def create_app(sa): api.add_route('/usercerts/{user_id}/{fingerprint}', models.UserCert()) api.add_route('/hostcerts', models.HostCerts()) api.add_route('/hostcerts/{host_id}/{fingerprint}', models.HostCert()) - api.add_route('/hosttokens', models.Token()) + api.add_route('/hosttokens', models.Tokens()) + api.add_route('/novavendordata', models.NovaVendorData()) return api diff --git a/tatu/api/models.py b/tatu/api/models.py index 0daf9ca..c87be4f 100644 --- a/tatu/api/models.py +++ b/tatu/api/models.py @@ -99,7 +99,7 @@ class HostCert(object): resp.body = json.dumps(body) resp.status = falcon.HTTP_OK -class Token(object): +class Tokens(object): def on_post(self, req, resp): body = None @@ -113,3 +113,39 @@ class Token(object): ) resp.status = falcon.HTTP_201 resp.location = '/hosttokens/' + token.token_id + +class NovaVendorData(object): + + def on_post(self, req, resp): + # An example of the data nova sends to vendordata services: + # { + # "hostname": "foo", + # "image-id": "75a74383-f276-4774-8074-8c4e3ff2ca64", + # "instance-id": "2ae914e9-f5ab-44ce-b2a2-dcf8373d899d", + # "metadata": {}, + # "project-id": "039d104b7a5c4631b4ba6524d0b9e981", + # "user-data": null + # } + body = None + if req.content_length: + body = json.load(req.stream) + token = db.createToken( + self.session, + body['instance-id'], + body['project-id'], + body['hostname'] + ) + auth = db.getAuthority(self.session, body['project-id']) + if auth is None: + resp.status = falcon.HTTP_NOT_FOUND + return + key = RSA.importKey(auth.user_key) + pub_key = key.publickey().exportKey('OpenSSH') + vendordata = { + 'token': token.token_id, + 'auth_pub_key_user': pub_key, + 'principals': 'admin' + } + resp.body = json.dumps(vendordata) + resp.location = '/hosttokens/' + token.token_id + resp.status = falcon.HTTP_201 diff --git a/tatu/tests/test_app.py b/tatu/tests/test_app.py index e56e316..27e4f7d 100644 --- a/tatu/tests/test_app.py +++ b/tatu/tests/test_app.py @@ -32,6 +32,7 @@ user_pub_key = user_key.publickey().exportKey('OpenSSH') user_fingerprint = sshpubkeys.SSHKey(user_pub_key).hash() auth_id = str(uuid.uuid4()) +auth_user_pub_key = None @pytest.mark.dependency() def test_post_authority(client, db): @@ -55,6 +56,8 @@ def test_get_authority(client): body = json.loads(response.content) assert 'auth_id' in body assert 'user_key.pub' in body + global auth_user_pub_key + auth_user_pub_key = body['user_key.pub'] assert 'host_key.pub' in body assert 'user_key' not in body assert 'host_key' not in body @@ -147,6 +150,30 @@ def host_request(token, host=host_id, pub_key=host_pub_key): 'key.pub': pub_key } +@pytest.mark.dependency(depends=['test_post_authority']) +def test_post_novavendordata(client, db): + host = str(uuid.uuid4()) + req = { + 'instance-id': host, + 'project-id': auth_id, + 'hostname': 'mytest.testing' + } + response = client.simulate_post( + '/novavendordata', + body=json.dumps(req) + ) + assert response.status == falcon.HTTP_CREATED + assert 'location' in response.headers + location_path = response.headers['location'].split('/') + assert location_path[1] == 'hosttokens' + vendordata = json.loads(response.content) + assert 'token' in vendordata + assert vendordata['token'] == location_path[-1] + assert 'auth_pub_key_user' in vendordata + assert vendordata['auth_pub_key_user'] == auth_user_pub_key + assert 'principals' in vendordata + assert vendordata['principals'] == 'admin' + @pytest.mark.dependency(depends=['test_post_authority']) def test_post_token_and_host(client, db): token = token_request()