Adds POST method support in the metadata service

Fixes bug: #1092311

The password generation feature introduced in Grizzly requires POST
support in the metadata service.

Support has been added to both the metadata agent and namespace proxy.
Tests have been added and updated accordingly.

Change-Id: If7ef6bede1775ad682822cb868518ec732b90805
This commit is contained in:
Alessandro Pilotti 2013-05-01 07:14:59 +03:00
parent dbae68c360
commit 86cf8b53ee
4 changed files with 90 additions and 13 deletions

View File

@ -139,7 +139,8 @@ class MetadataProxyHandler(object):
'')) ''))
h = httplib2.Http() h = httplib2.Http()
resp, content = h.request(url, headers=headers) resp, content = h.request(url, method=req.method, headers=headers,
body=req.body)
if resp.status == 200: if resp.status == 200:
LOG.debug(str(resp)) LOG.debug(str(resp))
@ -153,6 +154,8 @@ class MetadataProxyHandler(object):
return webob.exc.HTTPForbidden() return webob.exc.HTTPForbidden()
elif resp.status == 404: elif resp.status == 404:
return webob.exc.HTTPNotFound() return webob.exc.HTTPNotFound()
elif resp.status == 409:
return webob.exc.HTTPConflict()
elif resp.status == 500: elif resp.status == 500:
msg = _( msg = _(
'Remote metadata server experienced an internal server error.' 'Remote metadata server experienced an internal server error.'

View File

@ -75,15 +75,18 @@ class NetworkMetadataProxyHandler(object):
LOG.debug(_("Request: %s"), req) LOG.debug(_("Request: %s"), req)
try: try:
return self._proxy_request(req.remote_addr, return self._proxy_request(req.remote_addr,
req.method,
req.path_info, req.path_info,
req.query_string) req.query_string,
req.body)
except Exception: except Exception:
LOG.exception(_("Unexpected error.")) LOG.exception(_("Unexpected error."))
msg = _('An unknown error has occurred. ' msg = _('An unknown error has occurred. '
'Please try your request again.') 'Please try your request again.')
return webob.exc.HTTPInternalServerError(explanation=unicode(msg)) return webob.exc.HTTPInternalServerError(explanation=unicode(msg))
def _proxy_request(self, remote_address, path_info, query_string): def _proxy_request(self, remote_address, method, path_info,
query_string, body):
headers = { headers = {
'X-Forwarded-For': remote_address, 'X-Forwarded-For': remote_address,
} }
@ -103,7 +106,9 @@ class NetworkMetadataProxyHandler(object):
h = httplib2.Http() h = httplib2.Http()
resp, content = h.request( resp, content = h.request(
url, url,
method=method,
headers=headers, headers=headers,
body=body,
connection_type=UnixDomainHTTPConnection) connection_type=UnixDomainHTTPConnection)
if resp.status == 200: if resp.status == 200:
@ -112,6 +117,8 @@ class NetworkMetadataProxyHandler(object):
return content return content
elif resp.status == 404: elif resp.status == 404:
return webob.exc.HTTPNotFound() return webob.exc.HTTPNotFound()
elif resp.status == 409:
return webob.exc.HTTPConflict()
elif resp.status == 500: elif resp.status == 500:
msg = _( msg = _(
'Remote metadata server experienced an internal server error.' 'Remote metadata server experienced an internal server error.'

View File

@ -180,9 +180,12 @@ class TestMetadataProxyHandler(base.BaseTestCase):
self._get_instance_id_helper(headers, ports, networks=['the_id']) self._get_instance_id_helper(headers, ports, networks=['the_id'])
) )
def _proxy_request_test_helper(self, response_code): def _proxy_request_test_helper(self, response_code=200, method='GET'):
hdrs = {'X-Forwarded-For': '8.8.8.8'} hdrs = {'X-Forwarded-For': '8.8.8.8'}
req = mock.Mock(path_info='/the_path', query_string='', headers=hdrs) body = 'body'
req = mock.Mock(path_info='/the_path', query_string='', headers=hdrs,
method=method, body=body)
resp = mock.Mock(status=response_code) resp = mock.Mock(status=response_code)
with mock.patch.object(self.handler, '_sign_instance_id') as sign: with mock.patch.object(self.handler, '_sign_instance_id') as sign:
sign.return_value = 'signed' sign.return_value = 'signed'
@ -193,16 +196,22 @@ class TestMetadataProxyHandler(base.BaseTestCase):
mock_http.assert_has_calls([ mock_http.assert_has_calls([
mock.call().request( mock.call().request(
'http://9.9.9.9:8775/the_path', 'http://9.9.9.9:8775/the_path',
method=method,
headers={ headers={
'X-Forwarded-For': '8.8.8.8', 'X-Forwarded-For': '8.8.8.8',
'X-Instance-ID-Signature': 'signed', 'X-Instance-ID-Signature': 'signed',
'X-Instance-ID': 'the_id' 'X-Instance-ID': 'the_id'
} },
body=body
)] )]
) )
return retval return retval
def test_proxy_request_post(self):
self.assertEqual('content',
self._proxy_request_test_helper(method='POST'))
def test_proxy_request_200(self): def test_proxy_request_200(self):
self.assertEqual('content', self._proxy_request_test_helper(200)) self.assertEqual('content', self._proxy_request_test_helper(200))
@ -214,6 +223,10 @@ class TestMetadataProxyHandler(base.BaseTestCase):
self.assertIsInstance(self._proxy_request_test_helper(404), self.assertIsInstance(self._proxy_request_test_helper(404),
webob.exc.HTTPNotFound) webob.exc.HTTPNotFound)
def test_proxy_request_409(self):
self.assertIsInstance(self._proxy_request_test_helper(409),
webob.exc.HTTPConflict)
def test_proxy_request_500(self): def test_proxy_request_500(self):
self.assertIsInstance(self._proxy_request_test_helper(500), self.assertIsInstance(self._proxy_request_test_helper(500),
webob.exc.HTTPInternalServerError) webob.exc.HTTPInternalServerError)

View File

@ -74,8 +74,10 @@ class TestNetworkMetadataProxyHandler(base.BaseTestCase):
retval = self.handler(req) retval = self.handler(req)
self.assertEqual(retval, 'value') self.assertEqual(retval, 'value')
proxy_req.assert_called_once_with(req.remote_addr, proxy_req.assert_called_once_with(req.remote_addr,
req.method,
req.path_info, req.path_info,
req.query_string) req.query_string,
req.body)
def test_no_argument_passed_to_init(self): def test_no_argument_passed_to_init(self):
with testtools.ExpectedException(ValueError): with testtools.ExpectedException(ValueError):
@ -98,17 +100,21 @@ class TestNetworkMetadataProxyHandler(base.BaseTestCase):
mock_http.return_value.request.return_value = (resp, 'content') mock_http.return_value.request.return_value = (resp, 'content')
retval = self.handler._proxy_request('192.168.1.1', retval = self.handler._proxy_request('192.168.1.1',
'GET',
'/latest/meta-data', '/latest/meta-data',
'',
'') '')
mock_http.assert_has_calls([ mock_http.assert_has_calls([
mock.call().request( mock.call().request(
'http://169.254.169.254/latest/meta-data', 'http://169.254.169.254/latest/meta-data',
method='GET',
headers={ headers={
'X-Forwarded-For': '192.168.1.1', 'X-Forwarded-For': '192.168.1.1',
'X-Quantum-Router-ID': 'router_id' 'X-Quantum-Router-ID': 'router_id'
}, },
connection_type=ns_proxy.UnixDomainHTTPConnection connection_type=ns_proxy.UnixDomainHTTPConnection,
body=''
)] )]
) )
@ -122,17 +128,21 @@ class TestNetworkMetadataProxyHandler(base.BaseTestCase):
mock_http.return_value.request.return_value = (resp, 'content') mock_http.return_value.request.return_value = (resp, 'content')
retval = self.handler._proxy_request('192.168.1.1', retval = self.handler._proxy_request('192.168.1.1',
'GET',
'/latest/meta-data', '/latest/meta-data',
'',
'') '')
mock_http.assert_has_calls([ mock_http.assert_has_calls([
mock.call().request( mock.call().request(
'http://169.254.169.254/latest/meta-data', 'http://169.254.169.254/latest/meta-data',
method='GET',
headers={ headers={
'X-Forwarded-For': '192.168.1.1', 'X-Forwarded-For': '192.168.1.1',
'X-Quantum-Network-ID': 'network_id' 'X-Quantum-Network-ID': 'network_id'
}, },
connection_type=ns_proxy.UnixDomainHTTPConnection connection_type=ns_proxy.UnixDomainHTTPConnection,
body=''
)] )]
) )
@ -146,22 +156,54 @@ class TestNetworkMetadataProxyHandler(base.BaseTestCase):
mock_http.return_value.request.return_value = (resp, '') mock_http.return_value.request.return_value = (resp, '')
retval = self.handler._proxy_request('192.168.1.1', retval = self.handler._proxy_request('192.168.1.1',
'GET',
'/latest/meta-data', '/latest/meta-data',
'',
'') '')
mock_http.assert_has_calls([ mock_http.assert_has_calls([
mock.call().request( mock.call().request(
'http://169.254.169.254/latest/meta-data', 'http://169.254.169.254/latest/meta-data',
method='GET',
headers={ headers={
'X-Forwarded-For': '192.168.1.1', 'X-Forwarded-For': '192.168.1.1',
'X-Quantum-Network-ID': 'network_id' 'X-Quantum-Network-ID': 'network_id'
}, },
connection_type=ns_proxy.UnixDomainHTTPConnection connection_type=ns_proxy.UnixDomainHTTPConnection,
body=''
)] )]
) )
self.assertIsInstance(retval, webob.exc.HTTPNotFound) self.assertIsInstance(retval, webob.exc.HTTPNotFound)
def test_proxy_request_network_409(self):
self.handler.network_id = 'network_id'
resp = mock.Mock(status=409)
with mock.patch('httplib2.Http') as mock_http:
mock_http.return_value.request.return_value = (resp, '')
retval = self.handler._proxy_request('192.168.1.1',
'POST',
'/latest/meta-data',
'',
'')
mock_http.assert_has_calls([
mock.call().request(
'http://169.254.169.254/latest/meta-data',
method='POST',
headers={
'X-Forwarded-For': '192.168.1.1',
'X-Quantum-Network-ID': 'network_id'
},
connection_type=ns_proxy.UnixDomainHTTPConnection,
body=''
)]
)
self.assertIsInstance(retval, webob.exc.HTTPConflict)
def test_proxy_request_network_500(self): def test_proxy_request_network_500(self):
self.handler.network_id = 'network_id' self.handler.network_id = 'network_id'
@ -170,17 +212,21 @@ class TestNetworkMetadataProxyHandler(base.BaseTestCase):
mock_http.return_value.request.return_value = (resp, '') mock_http.return_value.request.return_value = (resp, '')
retval = self.handler._proxy_request('192.168.1.1', retval = self.handler._proxy_request('192.168.1.1',
'GET',
'/latest/meta-data', '/latest/meta-data',
'',
'') '')
mock_http.assert_has_calls([ mock_http.assert_has_calls([
mock.call().request( mock.call().request(
'http://169.254.169.254/latest/meta-data', 'http://169.254.169.254/latest/meta-data',
method='GET',
headers={ headers={
'X-Forwarded-For': '192.168.1.1', 'X-Forwarded-For': '192.168.1.1',
'X-Quantum-Network-ID': 'network_id' 'X-Quantum-Network-ID': 'network_id'
}, },
connection_type=ns_proxy.UnixDomainHTTPConnection connection_type=ns_proxy.UnixDomainHTTPConnection,
body=''
)] )]
) )
@ -195,17 +241,21 @@ class TestNetworkMetadataProxyHandler(base.BaseTestCase):
with testtools.ExpectedException(Exception): with testtools.ExpectedException(Exception):
self.handler._proxy_request('192.168.1.1', self.handler._proxy_request('192.168.1.1',
'GET',
'/latest/meta-data', '/latest/meta-data',
'',
'') '')
mock_http.assert_has_calls([ mock_http.assert_has_calls([
mock.call().request( mock.call().request(
'http://169.254.169.254/latest/meta-data', 'http://169.254.169.254/latest/meta-data',
method='GET',
headers={ headers={
'X-Forwarded-For': '192.168.1.1', 'X-Forwarded-For': '192.168.1.1',
'X-Quantum-Network-ID': 'network_id' 'X-Quantum-Network-ID': 'network_id'
}, },
connection_type=ns_proxy.UnixDomainHTTPConnection connection_type=ns_proxy.UnixDomainHTTPConnection,
body=''
)] )]
) )
@ -218,17 +268,21 @@ class TestNetworkMetadataProxyHandler(base.BaseTestCase):
with testtools.ExpectedException(Exception): with testtools.ExpectedException(Exception):
self.handler._proxy_request('192.168.1.1', self.handler._proxy_request('192.168.1.1',
'GET',
'/latest/meta-data', '/latest/meta-data',
'',
'') '')
mock_http.assert_has_calls([ mock_http.assert_has_calls([
mock.call().request( mock.call().request(
'http://169.254.169.254/latest/meta-data', 'http://169.254.169.254/latest/meta-data',
method='GET',
headers={ headers={
'X-Forwarded-For': '192.168.1.1', 'X-Forwarded-For': '192.168.1.1',
'X-Quantum-Network-ID': 'network_id' 'X-Quantum-Network-ID': 'network_id'
}, },
connection_type=ns_proxy.UnixDomainHTTPConnection connection_type=ns_proxy.UnixDomainHTTPConnection,
body=''
)] )]
) )