Use Zaqar signed URLs in software deployment
Instead of using a token that we need to create every time to poll the Zaqar queue, create a signature and use it as an authentication mechanism. Change-Id: Ibf9f6c334eba024f6faa7d6bb708d6d9f778ee43
This commit is contained in:
parent
6499a52e4c
commit
82f3817105
@ -17,7 +17,7 @@ from heat.common.i18n import _LE
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
from zaqarclient.queues.v1 import client as zaqarclient
|
||||
from zaqarclient.queues.v2 import client as zaqarclient
|
||||
from zaqarclient.transport import errors as zaqar_errors
|
||||
|
||||
from heat.engine.clients import client_plugin
|
||||
@ -53,9 +53,22 @@ class ZaqarClientPlugin(client_plugin.ClientPlugin):
|
||||
conf = {'auth_opts': auth_opts}
|
||||
endpoint = self.url_for(service_type=self.MESSAGING)
|
||||
|
||||
client = zaqarclient.Client(url=endpoint, conf=conf, version=1.1)
|
||||
return zaqarclient.Client(url=endpoint, conf=conf, version=2)
|
||||
|
||||
return client
|
||||
def create_from_signed_url(self, project_id, paths, expires, methods,
|
||||
signature):
|
||||
opts = {
|
||||
'paths': paths,
|
||||
'expires': expires,
|
||||
'methods': methods,
|
||||
'signature': signature,
|
||||
'os_project_id': project_id,
|
||||
}
|
||||
auth_opts = {'backend': 'signed-url',
|
||||
'options': opts}
|
||||
conf = {'auth_opts': auth_opts}
|
||||
endpoint = self.url_for(service_type=self.MESSAGING)
|
||||
return zaqarclient.Client(url=endpoint, conf=conf, version=2)
|
||||
|
||||
def is_not_found(self, ex):
|
||||
return isinstance(ex, zaqar_errors.ResourceNotFound)
|
||||
|
@ -290,6 +290,14 @@ class SignalResponder(stack_user.StackUser):
|
||||
self._create_user()
|
||||
|
||||
queue_id = self.physical_resource_name()
|
||||
zaqar_plugin = self.client_plugin('zaqar')
|
||||
zaqar = zaqar_plugin.create_for_tenant(
|
||||
self.stack.stack_user_project_id, self._user_token())
|
||||
queue = zaqar.queue(queue_id)
|
||||
signed_url_data = queue.signed_url(
|
||||
['messages'], methods=['GET', 'DELETE'])
|
||||
self.data_set('zaqar_queue_signed_url_data',
|
||||
jsonutils.dumps(signed_url_data))
|
||||
self.data_set('zaqar_signal_queue_id', queue_id)
|
||||
return queue_id
|
||||
|
||||
|
@ -118,10 +118,8 @@ class SoftwareConfigService(service.Service):
|
||||
requests.put(metadata_put_url, json_md)
|
||||
if metadata_queue_id:
|
||||
project = stack_user_project_id
|
||||
token = self._get_user_token(cnxt, rs, project)
|
||||
queue = self._get_zaqar_queue(cnxt, rs, project, metadata_queue_id)
|
||||
zaqar_plugin = cnxt.clients.client_plugin('zaqar')
|
||||
zaqar = zaqar_plugin.create_for_tenant(project, token)
|
||||
queue = zaqar.queue(metadata_queue_id)
|
||||
queue.post({'body': md, 'ttl': zaqar_plugin.DEFAULT_TTL})
|
||||
|
||||
def _refresh_swift_software_deployment(self, cnxt, sd, deploy_signal_id):
|
||||
@ -170,24 +168,32 @@ class SoftwareConfigService(service.Service):
|
||||
return software_deployment_object.SoftwareDeployment.get_by_id(
|
||||
cnxt, sd.id)
|
||||
|
||||
def _get_user_token(self, cnxt, rs, project):
|
||||
user = password = None
|
||||
def _get_zaqar_queue(self, cnxt, rs, project, queue_name):
|
||||
user = password = signed_url_data = None
|
||||
for rd in rs.data:
|
||||
if rd.key == 'password':
|
||||
password = crypt.decrypt(rd.decrypt_method, rd.value)
|
||||
if rd.key == 'user_id':
|
||||
user = rd.value
|
||||
keystone = cnxt.clients.client('keystone')
|
||||
return keystone.stack_domain_user_token(
|
||||
user_id=user, project_id=project, password=password)
|
||||
if rd.key == 'zaqar_queue_signed_url_data':
|
||||
signed_url_data = jsonutils.loads(rd.value)
|
||||
zaqar_plugin = cnxt.clients.client_plugin('zaqar')
|
||||
if signed_url_data is None:
|
||||
keystone = cnxt.clients.client('keystone')
|
||||
token = keystone.stack_domain_user_token(
|
||||
user_id=user, project_id=project, password=password)
|
||||
zaqar = zaqar_plugin.create_for_tenant(project, token)
|
||||
else:
|
||||
signed_url_data.pop('project')
|
||||
zaqar = zaqar_plugin.create_from_signed_url(project,
|
||||
**signed_url_data)
|
||||
|
||||
return zaqar.queue(queue_name)
|
||||
|
||||
def _refresh_zaqar_software_deployment(self, cnxt, sd, deploy_queue_id):
|
||||
rs = db_api.resource_get_by_physical_resource_id(cnxt, sd.server_id)
|
||||
rs = db_api.resource_get_by_physical_resource_id(cnxt, sd.id)
|
||||
project = sd.stack_user_project_id
|
||||
token = self._get_user_token(cnxt, rs, project)
|
||||
zaqar_plugin = cnxt.clients.client_plugin('zaqar')
|
||||
zaqar = zaqar_plugin.create_for_tenant(project, token)
|
||||
queue = zaqar.queue(deploy_queue_id)
|
||||
queue = self._get_zaqar_queue(cnxt, rs, project, deploy_queue_id)
|
||||
|
||||
messages = list(queue.pop())
|
||||
if messages:
|
||||
|
@ -26,7 +26,7 @@ class ZaqarClientPluginTest(common.HeatTestCase):
|
||||
plugin = context.clients.client_plugin('zaqar')
|
||||
client = plugin.client()
|
||||
self.assertEqual('http://server.test:5000/v3', client.api_url)
|
||||
self.assertEqual(1.1, client.api_version)
|
||||
self.assertEqual(2.0, client.api_version)
|
||||
self.assertEqual('test_tenant_id',
|
||||
client.conf['auth_opts']['options']['os_project_id'])
|
||||
|
||||
|
@ -199,6 +199,7 @@ class SignalResource(signal_responder.SignalResponder):
|
||||
'signal': attributes.Schema('Get a signal')}
|
||||
|
||||
def handle_create(self):
|
||||
self.password = 'password'
|
||||
super(SignalResource, self).handle_create()
|
||||
self.resource_id_set(self._get_user_id())
|
||||
|
||||
|
@ -18,6 +18,8 @@ import uuid
|
||||
import mock
|
||||
import six
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
from heat.common import exception as exc
|
||||
from heat.common.i18n import _
|
||||
from heat.engine.clients.os import nova
|
||||
@ -1169,6 +1171,17 @@ class SoftwareDeploymentTest(common.HeatTestCase):
|
||||
def test_get_zaqar_queue(self):
|
||||
dep_data = {}
|
||||
|
||||
zc = mock.MagicMock()
|
||||
zcc = self.patch(
|
||||
'heat.engine.clients.os.zaqar.ZaqarClientPlugin.create_for_tenant')
|
||||
zcc.return_value = zc
|
||||
|
||||
mock_queue = mock.MagicMock()
|
||||
zc.queue.return_value = mock_queue
|
||||
|
||||
signed_data = {"signature": "hi", "expires": "later"}
|
||||
mock_queue.signed_url.return_value = signed_data
|
||||
|
||||
self._create_stack(self.template_zaqar_signal)
|
||||
|
||||
def data_set(key, value, redact=False):
|
||||
@ -1183,6 +1196,8 @@ class SoftwareDeploymentTest(common.HeatTestCase):
|
||||
|
||||
queue_id = self.deployment._get_zaqar_signal_queue_id()
|
||||
self.assertEqual(queue_id, dep_data['zaqar_signal_queue_id'])
|
||||
self.assertEqual(jsonutils.dumps(signed_data),
|
||||
dep_data['zaqar_queue_signed_url_data'])
|
||||
|
||||
self.assertEqual(queue_id,
|
||||
self.deployment._get_zaqar_signal_queue_id())
|
||||
|
@ -14,6 +14,7 @@
|
||||
import datetime
|
||||
import uuid
|
||||
|
||||
import mock
|
||||
import mox
|
||||
from oslo_serialization import jsonutils as json
|
||||
from oslo_utils import timeutils
|
||||
@ -480,7 +481,8 @@ class HeatWaitConditionTest(common.HeatTestCase):
|
||||
signal = json.loads(handle.FnGetAtt('signal'))
|
||||
self.assertIn('alarm_url', signal)
|
||||
|
||||
def test_getatt_signal_zaqar(self):
|
||||
@mock.patch('zaqarclient.queues.v2.queues.Queue.signed_url')
|
||||
def test_getatt_signal_zaqar(self, mock_signed_url):
|
||||
handle = self._create_heat_handle(
|
||||
template=test_template_heat_waithandle_zaqar)
|
||||
self.assertIsNone(handle.FnGetAtt('token'))
|
||||
|
@ -120,8 +120,9 @@ class SignalTest(common.HeatTestCase):
|
||||
self.assertEqual('anaccesskey', rs_data.get('access_key'))
|
||||
self.assertEqual('verysecret', rs_data.get('secret_key'))
|
||||
self.assertEqual('1234', rs_data.get('user_id'))
|
||||
self.assertEqual('password', rs_data.get('password'))
|
||||
self.assertEqual(rsrc.resource_id, rs_data.get('user_id'))
|
||||
self.assertEqual(4, len(rs_data))
|
||||
self.assertEqual(5, len(rs_data))
|
||||
|
||||
def test_get_user_id(self):
|
||||
# Setup
|
||||
@ -259,7 +260,8 @@ class SignalTest(common.HeatTestCase):
|
||||
mock_has.assert_called_once_with('signal_handler')
|
||||
self.assertEqual(second_url, 'cached')
|
||||
|
||||
def test_FnGetAtt_zaqar_signal(self):
|
||||
@mock.patch('zaqarclient.queues.v2.queues.Queue.signed_url')
|
||||
def test_FnGetAtt_zaqar_signal(self, mock_signed_url):
|
||||
# Setup
|
||||
stack = self._create_stack(TEMPLATE_ZAQAR_SIGNAL)
|
||||
rsrc = stack['signal_handler']
|
||||
@ -276,10 +278,14 @@ class SignalTest(common.HeatTestCase):
|
||||
self.assertIn('username', signal)
|
||||
self.assertIn('password', signal)
|
||||
self.assertIn('queue_id', signal)
|
||||
mock_signed_url.assert_called_once_with(
|
||||
['messages'], methods=['GET', 'DELETE'])
|
||||
|
||||
@mock.patch.object(stk.Stack, 'cache_data_resource_attribute')
|
||||
@mock.patch.object(stk.Stack, 'has_cache_data')
|
||||
def test_FnGetAtt_zaqar_signal_is_cached(self, mock_has, mock_get):
|
||||
@mock.patch('zaqarclient.queues.v2.queues.Queue.signed_url')
|
||||
def test_FnGetAtt_zaqar_signal_is_cached(self, mock_signed_url, mock_has,
|
||||
mock_get):
|
||||
# Setup
|
||||
mock_has.return_value = False
|
||||
stack = self._create_stack(TEMPLATE_ZAQAR_SIGNAL)
|
||||
@ -454,7 +460,8 @@ class SignalTest(common.HeatTestCase):
|
||||
self.assertEqual(1, mock_delete_container.call_count)
|
||||
self.assertEqual(1, mock_head.call_count)
|
||||
|
||||
def test_FnGetAtt_zaqar_signal_delete(self):
|
||||
@mock.patch('zaqarclient.queues.v2.queues.Queue.signed_url')
|
||||
def test_FnGetAtt_zaqar_signal_delete(self, mock_signed_url):
|
||||
# Setup
|
||||
stack = self._create_stack(TEMPLATE_ZAQAR_SIGNAL)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user