Merge "Move signals to SignalResponder class"
This commit is contained in:
commit
8fe749e8a0
@ -13,7 +13,6 @@
|
||||
|
||||
import copy
|
||||
import six
|
||||
import uuid
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
@ -315,63 +314,6 @@ class SoftwareDeployment(signal_responder.SignalResponder):
|
||||
def _build_derived_options(self, action, source):
|
||||
return source.get(sc.SoftwareConfig.OPTIONS)
|
||||
|
||||
def _get_temp_url(self):
|
||||
put_url = self.data().get('signal_temp_url')
|
||||
if put_url:
|
||||
return put_url
|
||||
|
||||
container = self.physical_resource_name()
|
||||
object_name = str(uuid.uuid4())
|
||||
|
||||
self.client('swift').put_container(container)
|
||||
|
||||
put_url = self.client_plugin('swift').get_temp_url(
|
||||
container, object_name)
|
||||
self.data_set('signal_temp_url', put_url)
|
||||
self.data_set('signal_object_name', object_name)
|
||||
|
||||
self.client('swift').put_object(
|
||||
container, object_name, '')
|
||||
return put_url
|
||||
|
||||
def _get_queue_id(self):
|
||||
queue_id = self.data().get('signal_queue_id')
|
||||
if queue_id:
|
||||
return queue_id
|
||||
|
||||
queue_id = self.physical_resource_name()
|
||||
zaqar = self.client('zaqar')
|
||||
zaqar.queue(queue_id).ensure_exists()
|
||||
self.data_set('signal_queue_id', queue_id)
|
||||
return queue_id
|
||||
|
||||
def _delete_temp_url(self):
|
||||
object_name = self.data().get('signal_object_name')
|
||||
if not object_name:
|
||||
return
|
||||
try:
|
||||
container = self.physical_resource_name()
|
||||
swift = self.client('swift')
|
||||
swift.delete_object(container, object_name)
|
||||
headers = swift.head_container(container)
|
||||
if int(headers['x-container-object-count']) == 0:
|
||||
swift.delete_container(container)
|
||||
except Exception as ex:
|
||||
self.client_plugin('swift').ignore_not_found(ex)
|
||||
self.data_delete('signal_object_name')
|
||||
self.data_delete('signal_temp_url')
|
||||
|
||||
def _delete_queue(self):
|
||||
queue_id = self.data().get('signal_queue_id')
|
||||
if not queue_id:
|
||||
return
|
||||
zaqar = self.client('zaqar')
|
||||
try:
|
||||
zaqar.queue(queue_id).delete()
|
||||
except Exception as ex:
|
||||
self.client_plugin('zaqar').ignore_not_found(ex)
|
||||
self.data_delete('signal_queue_id')
|
||||
|
||||
def _build_derived_inputs(self, action, source):
|
||||
scl = sc.SoftwareConfig
|
||||
inputs = copy.deepcopy(source.get(scl.INPUTS)) or []
|
||||
@ -440,7 +382,7 @@ class SoftwareDeployment(signal_responder.SignalResponder):
|
||||
scl.DESCRIPTION: _('ID of signal to use for signaling '
|
||||
'output values'),
|
||||
scl.TYPE: 'String',
|
||||
'value': self._get_temp_url()
|
||||
'value': self._get_swift_signal_url()
|
||||
})
|
||||
inputs.append({
|
||||
scl.NAME: self.DEPLOY_SIGNAL_VERB,
|
||||
@ -450,31 +392,32 @@ class SoftwareDeployment(signal_responder.SignalResponder):
|
||||
'value': 'PUT'
|
||||
})
|
||||
elif self._signal_transport_heat() or self._signal_transport_zaqar():
|
||||
creds = self._get_heat_signal_credentials()
|
||||
inputs.extend([{
|
||||
scl.NAME: self.DEPLOY_AUTH_URL,
|
||||
scl.DESCRIPTION: _('URL for API authentication'),
|
||||
scl.TYPE: 'String',
|
||||
'value': self.keystone().v3_endpoint
|
||||
'value': creds['auth_url']
|
||||
}, {
|
||||
scl.NAME: self.DEPLOY_USERNAME,
|
||||
scl.DESCRIPTION: _('Username for API authentication'),
|
||||
scl.TYPE: 'String',
|
||||
'value': self.physical_resource_name(),
|
||||
'value': creds['username']
|
||||
}, {
|
||||
scl.NAME: self.DEPLOY_USER_ID,
|
||||
scl.DESCRIPTION: _('User ID for API authentication'),
|
||||
scl.TYPE: 'String',
|
||||
'value': self._get_user_id(),
|
||||
'value': creds['user_id']
|
||||
}, {
|
||||
scl.NAME: self.DEPLOY_PASSWORD,
|
||||
scl.DESCRIPTION: _('Password for API authentication'),
|
||||
scl.TYPE: 'String',
|
||||
'value': self.password
|
||||
'value': creds['password']
|
||||
}, {
|
||||
scl.NAME: self.DEPLOY_PROJECT_ID,
|
||||
scl.DESCRIPTION: _('ID of project for API authentication'),
|
||||
scl.TYPE: 'String',
|
||||
'value': self.stack.stack_user_project_id
|
||||
'value': creds['project_id']
|
||||
}])
|
||||
if self._signal_transport_zaqar():
|
||||
inputs.append({
|
||||
@ -482,31 +425,14 @@ class SoftwareDeployment(signal_responder.SignalResponder):
|
||||
scl.DESCRIPTION: _('ID of queue to use for signaling '
|
||||
'output values'),
|
||||
scl.TYPE: 'String',
|
||||
'value': self._get_queue_id()
|
||||
'value': self._get_zaqar_signal_queue_id()
|
||||
})
|
||||
|
||||
return inputs
|
||||
|
||||
def handle_create(self):
|
||||
if self._signal_transport_cfn():
|
||||
self._create_user()
|
||||
self._create_keypair()
|
||||
if self._signal_transport_heat() or self._signal_transport_zaqar():
|
||||
self.password = uuid.uuid4().hex
|
||||
self._create_user()
|
||||
return self._handle_action(self.CREATE)
|
||||
|
||||
@property
|
||||
def password(self):
|
||||
return self.data().get('password')
|
||||
|
||||
@password.setter
|
||||
def password(self, password):
|
||||
if password is None:
|
||||
self.data_delete('password')
|
||||
else:
|
||||
self.data_set('password', password, True)
|
||||
|
||||
def check_create_complete(self, sd):
|
||||
if not sd:
|
||||
return True
|
||||
@ -536,15 +462,8 @@ class SoftwareDeployment(signal_responder.SignalResponder):
|
||||
return True
|
||||
|
||||
def _delete_resource(self):
|
||||
if self._signal_transport_cfn():
|
||||
self._delete_ec2_signed_url()
|
||||
self._delete_user()
|
||||
elif self._signal_transport_heat():
|
||||
self._delete_user()
|
||||
elif self._signal_transport_temp_url():
|
||||
self._delete_temp_url()
|
||||
elif self._signal_transport_zaqar():
|
||||
self._delete_queue()
|
||||
self._delete_signals()
|
||||
self._delete_user()
|
||||
|
||||
derived_config_id = None
|
||||
if self.resource_id is not None:
|
||||
|
@ -11,6 +11,8 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import uuid
|
||||
|
||||
from keystoneclient.contrib.ec2 import utils as ec2_utils
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
@ -36,17 +38,42 @@ class SignalResponder(stack_user.StackUser):
|
||||
# API operations as a consequence of handling a signal
|
||||
requires_deferred_auth = True
|
||||
|
||||
def handle_create(self):
|
||||
super(SignalResponder, self).handle_create()
|
||||
self._create_keypair()
|
||||
|
||||
def handle_delete(self):
|
||||
self._delete_signals()
|
||||
super(SignalResponder, self).handle_delete()
|
||||
|
||||
def _delete_signals(self):
|
||||
self._delete_ec2_signed_url()
|
||||
self._delete_heat_signal_url()
|
||||
self._delete_swift_signal_url()
|
||||
self._delete_zaqar_signal_queue()
|
||||
|
||||
def _delete_ec2_signed_url(self):
|
||||
self.data_delete('ec2_signed_url')
|
||||
@property
|
||||
def password(self):
|
||||
return self.data().get('password')
|
||||
|
||||
@password.setter
|
||||
def password(self, password):
|
||||
if password is None:
|
||||
self.data_delete('password')
|
||||
else:
|
||||
self.data_set('password', password, True)
|
||||
|
||||
def _get_heat_signal_credentials(self):
|
||||
"""Return OpenStack credentials that can be used to send a signal.
|
||||
|
||||
These credentials are for the user associated with this resource in
|
||||
the heat stack user domain.
|
||||
"""
|
||||
if self._get_user_id() is None:
|
||||
if self.password is None:
|
||||
self.password = uuid.uuid4().hex
|
||||
self._create_user()
|
||||
return {'auth_url': self.keystone().v3_endpoint,
|
||||
'username': self.physical_resource_name(),
|
||||
'user_id': self._get_user_id(),
|
||||
'password': self.password,
|
||||
'project_id': self.stack.stack_user_project_id}
|
||||
|
||||
def _get_ec2_signed_url(self, signal_type=SIGNAL):
|
||||
"""Create properly formatted and pre-signed URL.
|
||||
@ -65,9 +92,19 @@ class SignalResponder(stack_user.StackUser):
|
||||
secret_key = self.data().get('secret_key')
|
||||
|
||||
if not access_key or not secret_key:
|
||||
LOG.warn(_LW('Cannot generate signed url, '
|
||||
'no stored access/secret key'))
|
||||
return
|
||||
if self.id is None or self.action == self.DELETE:
|
||||
# it is either too early or too late to do this
|
||||
return
|
||||
if self._get_user_id() is None:
|
||||
self._create_user()
|
||||
self._create_keypair()
|
||||
access_key = self.data().get('access_key')
|
||||
secret_key = self.data().get('secret_key')
|
||||
|
||||
if not access_key or not secret_key:
|
||||
LOG.warn(_LW('Cannot generate signed url, '
|
||||
'unable to create keypair'))
|
||||
return
|
||||
|
||||
config_url = cfg.CONF.heat_waitcondition_server_url
|
||||
if config_url:
|
||||
@ -106,14 +143,23 @@ class SignalResponder(stack_user.StackUser):
|
||||
self.data_set('ec2_signed_url', url)
|
||||
return url
|
||||
|
||||
def _delete_heat_signal_url(self):
|
||||
self.data_delete('heat_signal_url')
|
||||
def _delete_ec2_signed_url(self):
|
||||
self.data_delete('ec2_signed_url')
|
||||
self._delete_keypair()
|
||||
|
||||
def _get_heat_signal_url(self):
|
||||
"""Return a heat-api signal URL for this resource.
|
||||
|
||||
This URL is not pre-signed, valid user credentials are required.
|
||||
"""
|
||||
stored = self.data().get('heat_signal_url')
|
||||
if stored is not None:
|
||||
return stored
|
||||
|
||||
if self.id is None or self.action == self.DELETE:
|
||||
# it is either too early or too late to do this
|
||||
return
|
||||
|
||||
url = self.client_plugin('heat').get_heat_url()
|
||||
host_url = urlparse.urlparse(url)
|
||||
path = self.identifier().url_path()
|
||||
@ -123,3 +169,84 @@ class SignalResponder(stack_user.StackUser):
|
||||
|
||||
self.data_set('heat_signal_url', url)
|
||||
return url
|
||||
|
||||
def _delete_heat_signal_url(self):
|
||||
self.data_delete('heat_signal_url')
|
||||
|
||||
def _get_swift_signal_url(self):
|
||||
"""Create properly formatted and pre-signed Swift signal URL.
|
||||
|
||||
This uses a Swift pre-signed temp_url.
|
||||
"""
|
||||
put_url = self.data().get('swift_signal_url')
|
||||
if put_url:
|
||||
return put_url
|
||||
|
||||
if self.id is None or self.action == self.DELETE:
|
||||
# it is either too early or too late to do this
|
||||
return
|
||||
|
||||
container = self.stack.id
|
||||
object_name = self.physical_resource_name()
|
||||
|
||||
self.client('swift').put_container(container)
|
||||
|
||||
put_url = self.client_plugin('swift').get_temp_url(
|
||||
container, object_name)
|
||||
self.data_set('swift_signal_url', put_url)
|
||||
self.data_set('swift_signal_object_name', object_name)
|
||||
|
||||
self.client('swift').put_object(
|
||||
container, object_name, '')
|
||||
return put_url
|
||||
|
||||
def _delete_swift_signal_url(self):
|
||||
object_name = self.data().get('swift_signal_object_name')
|
||||
if not object_name:
|
||||
return
|
||||
try:
|
||||
container = self.physical_resource_name()
|
||||
swift = self.client('swift')
|
||||
swift.delete_object(container, object_name)
|
||||
headers = swift.head_container(container)
|
||||
if int(headers['x-container-object-count']) == 0:
|
||||
swift.delete_container(container)
|
||||
except Exception as ex:
|
||||
self.client_plugin('swift').ignore_not_found(ex)
|
||||
self.data_delete('swift_signal_object_name')
|
||||
self.data_delete('swift_signal_url')
|
||||
|
||||
def _get_zaqar_signal_queue_id(self):
|
||||
"""Return a zaqar queue_id for signaling this resource.
|
||||
|
||||
This uses the created user for the credentials.
|
||||
"""
|
||||
queue_id = self.data().get('zaqar_signal_queue_id')
|
||||
if queue_id:
|
||||
return queue_id
|
||||
|
||||
if self.id is None or self.action == self.DELETE:
|
||||
# it is either too early or too late to do this
|
||||
return
|
||||
|
||||
if self._get_user_id() is None:
|
||||
if self.password is None:
|
||||
self.password = uuid.uuid4().hex
|
||||
self._create_user()
|
||||
|
||||
queue_id = self.physical_resource_name()
|
||||
zaqar = self.client('zaqar')
|
||||
zaqar.queue(queue_id).ensure_exists()
|
||||
self.data_set('zaqar_signal_queue_id', queue_id)
|
||||
return queue_id
|
||||
|
||||
def _delete_zaqar_signal_queue(self):
|
||||
queue_id = self.data().get('zaqar_signal_queue_id')
|
||||
if not queue_id:
|
||||
return
|
||||
zaqar = self.client('zaqar')
|
||||
try:
|
||||
zaqar.queue(queue_id).delete()
|
||||
except Exception as ex:
|
||||
self.client_plugin('zaqar').ignore_not_found(ex)
|
||||
self.data_delete('zaqar_signal_queue_id')
|
||||
|
@ -25,7 +25,7 @@ LOG = logging.getLogger(__name__)
|
||||
class StackUser(resource.Resource):
|
||||
|
||||
# Subclasses create a user, and optionally keypair associated with a
|
||||
# resource in a stack. Users are created in the heat stack user domain
|
||||
# resource in a stack. Users are created in the heat stack user domain
|
||||
# (in a project specific to the stack)
|
||||
def __init__(self, name, json_snippet, stack):
|
||||
super(StackUser, self).__init__(name, json_snippet, stack)
|
||||
@ -34,6 +34,10 @@ class StackUser(resource.Resource):
|
||||
self._create_user()
|
||||
|
||||
def _create_user(self):
|
||||
if self.data().get('user_id'):
|
||||
# a user has been created already
|
||||
return
|
||||
|
||||
# Check for stack user project, create if not yet set
|
||||
if not self.stack.stack_user_project_id:
|
||||
project_id = self.keystone().create_stack_domain_project(
|
||||
@ -85,6 +89,10 @@ class StackUser(resource.Resource):
|
||||
user_id = self._get_user_id()
|
||||
if user_id is None:
|
||||
return
|
||||
|
||||
# the user is going away, so we want the keypair gone as well
|
||||
self._delete_keypair()
|
||||
|
||||
try:
|
||||
self.keystone().delete_stack_domain_user(
|
||||
user_id=user_id, project_id=self.stack.stack_user_project_id)
|
||||
@ -100,8 +108,7 @@ class StackUser(resource.Resource):
|
||||
self.keystone().delete_stack_user(user_id)
|
||||
except kc_exception.NotFound:
|
||||
pass
|
||||
for data_key in ('credential_id', 'access_key', 'secret_key'):
|
||||
self.data_delete(data_key)
|
||||
self.data_delete('user_id')
|
||||
|
||||
def handle_suspend(self):
|
||||
user_id = self._get_user_id()
|
||||
@ -125,6 +132,9 @@ class StackUser(resource.Resource):
|
||||
# Subclasses may optionally call this in handle_create to create
|
||||
# an ec2 keypair associated with the user, the resulting keys are
|
||||
# stored in resource_data
|
||||
if self.data().get('credential_id'):
|
||||
return # a keypair was created already
|
||||
|
||||
user_id = self._get_user_id()
|
||||
kp = self.keystone().create_stack_domain_user_keypair(
|
||||
user_id=user_id, project_id=self.stack.stack_user_project_id)
|
||||
@ -146,10 +156,12 @@ class StackUser(resource.Resource):
|
||||
def _delete_keypair(self):
|
||||
# Subclasses may optionally call this to delete a keypair created
|
||||
# via _create_keypair
|
||||
user_id = self._get_user_id()
|
||||
credential_id = self.data().get('credential_id')
|
||||
if not credential_id:
|
||||
return
|
||||
user_id = self._get_user_id()
|
||||
if user_id is None:
|
||||
return
|
||||
|
||||
try:
|
||||
self.keystone().delete_stack_domain_user_keypair(
|
||||
|
@ -109,6 +109,7 @@ class FakeKeystoneClient(object):
|
||||
self.credential_id = credential_id
|
||||
self.token = auth_token
|
||||
self.context = context
|
||||
self.v3_endpoint = 'http://localhost:5000/v3'
|
||||
|
||||
class FakeCred(object):
|
||||
id = self.credential_id
|
||||
|
@ -136,8 +136,11 @@ class ResourceWithRequiredProps(GenericResource):
|
||||
|
||||
|
||||
class SignalResource(signal_responder.SignalResponder):
|
||||
properties_schema = {}
|
||||
attributes_schema = {'AlarmUrl': attributes.Schema('Get a signed webhook')}
|
||||
properties_schema = {
|
||||
'signal_transport': properties.Schema(properties.Schema.STRING,
|
||||
default='CFN_SIGNAL')}
|
||||
attributes_schema = {'AlarmUrl': attributes.Schema('Get a signed webhook'),
|
||||
'signal': attributes.Schema('Get a signal')}
|
||||
|
||||
def handle_create(self):
|
||||
super(SignalResource, self).handle_create()
|
||||
@ -148,8 +151,22 @@ class SignalResource(signal_responder.SignalResponder):
|
||||
{'type': self.type(), 'details': details})
|
||||
|
||||
def _resolve_attribute(self, name):
|
||||
if name == 'AlarmUrl' and self.resource_id is not None:
|
||||
return six.text_type(self._get_ec2_signed_url())
|
||||
if self.resource_id is not None:
|
||||
if self.properties['signal_transport'] == 'CFN_SIGNAL':
|
||||
d = {'alarm_url': six.text_type(self._get_ec2_signed_url())}
|
||||
elif self.properties['signal_transport'] == 'HEAT_SIGNAL':
|
||||
d = self._get_heat_signal_credentials()
|
||||
d['alarm_url'] = six.text_type(self._get_heat_signal_url())
|
||||
elif self.properties['signal_transport'] == 'TEMP_URL_SIGNAL':
|
||||
d = {'alarm_url': six.text_type(self._get_swift_signal_url())}
|
||||
elif self.properties['signal_transport'] == 'ZAQAR_SIGNAL':
|
||||
d = self._get_heat_signal_credentials()
|
||||
d['queue_id'] = six.text_type(
|
||||
self._get_zaqar_signal_queue_id())
|
||||
if name == 'AlarmUrl':
|
||||
return d['alarm_url']
|
||||
elif name == 'signal':
|
||||
return d
|
||||
|
||||
|
||||
class StackUserResource(stack_user.StackUser):
|
||||
|
@ -12,14 +12,15 @@
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
import six
|
||||
import uuid
|
||||
|
||||
from keystoneclient import exceptions as kc_exceptions
|
||||
import mox
|
||||
import six
|
||||
|
||||
from heat.common import exception
|
||||
from heat.common import template_format
|
||||
from heat.engine import resource
|
||||
from heat.engine.resources import stack_user
|
||||
from heat.engine import scheduler
|
||||
from heat.engine import stack as parser
|
||||
from heat.engine import template
|
||||
@ -30,13 +31,53 @@ from heat.tests import generic_resource
|
||||
from heat.tests import utils
|
||||
|
||||
|
||||
test_template_signal = '''
|
||||
test_cfn_template_signal = '''
|
||||
{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
"Description" : "Just a test.",
|
||||
"Parameters" : {},
|
||||
"Resources" : {
|
||||
"signal_handler" : {"Type" : "SignalResourceType"},
|
||||
"signal_handler" : {"Type" : "SignalResourceType",
|
||||
"Properties": {"signal_transport": "CFN_SIGNAL"}},
|
||||
"resource_X" : {"Type" : "GenericResourceType"}
|
||||
}
|
||||
}
|
||||
'''
|
||||
|
||||
test_heat_template_signal = '''
|
||||
{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
"Description" : "Just a test.",
|
||||
"Parameters" : {},
|
||||
"Resources" : {
|
||||
"signal_handler" : {"Type" : "SignalResourceType",
|
||||
"Properties": {"signal_transport": "HEAT_SIGNAL"}},
|
||||
"resource_X" : {"Type" : "GenericResourceType"}
|
||||
}
|
||||
}
|
||||
'''
|
||||
|
||||
test_swift_template_signal = '''
|
||||
{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
"Description" : "Just a test.",
|
||||
"Parameters" : {},
|
||||
"Resources" : {
|
||||
"signal_handler" : {"Type" : "SignalResourceType",
|
||||
"Properties": {"signal_transport": "TEMP_URL_SIGNAL"}},
|
||||
"resource_X" : {"Type" : "GenericResourceType"}
|
||||
}
|
||||
}
|
||||
'''
|
||||
|
||||
test_zaqar_template_signal = '''
|
||||
{
|
||||
"AWSTemplateFormatVersion" : "2010-09-09",
|
||||
"Description" : "Just a test.",
|
||||
"Parameters" : {},
|
||||
"Resources" : {
|
||||
"signal_handler" : {"Type" : "SignalResourceType",
|
||||
"Properties": {"signal_transport": "ZAQAR_SIGNAL"}},
|
||||
"resource_X" : {"Type" : "GenericResourceType"}
|
||||
}
|
||||
}
|
||||
@ -52,11 +93,12 @@ class SignalTest(common.HeatTestCase):
|
||||
def tearDown(self):
|
||||
super(SignalTest, self).tearDown()
|
||||
|
||||
def create_stack(self, stack_name='test_stack', stub=True):
|
||||
templ = template.Template(template_format.parse(test_template_signal))
|
||||
def create_stack(self, templ=test_cfn_template_signal,
|
||||
stack_name='test_stack', stub=True):
|
||||
tpl = template.Template(template_format.parse(templ))
|
||||
ctx = utils.dummy_context()
|
||||
ctx.tenant_id = 'test_tenant'
|
||||
stack = parser.Stack(ctx, stack_name, templ,
|
||||
stack = parser.Stack(ctx, stack_name, tpl,
|
||||
disable_rollback=True)
|
||||
|
||||
# Stub out the stack ID so we have a known value
|
||||
@ -67,23 +109,6 @@ class SignalTest(common.HeatTestCase):
|
||||
|
||||
return stack
|
||||
|
||||
def test_handle_create_fail_keypair_raise(self):
|
||||
self.stack = self.create_stack(stack_name='create_fail_keypair')
|
||||
|
||||
self.m.StubOutWithMock(stack_user.StackUser, '_create_keypair')
|
||||
stack_user.StackUser._create_keypair().AndRaise(Exception('Failed'))
|
||||
self.m.ReplayAll()
|
||||
|
||||
self.stack.create()
|
||||
|
||||
rsrc = self.stack['signal_handler']
|
||||
rs_data = resource_data_object.ResourceData.get_all(rsrc)
|
||||
self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state)
|
||||
self.assertIn('Failed', rsrc.status_reason)
|
||||
self.assertEqual('1234', rs_data.get('user_id'))
|
||||
self.assertIsNone(rsrc.resource_id)
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_resource_data(self):
|
||||
self.stub_keystoneclient(
|
||||
access='anaccesskey',
|
||||
@ -97,6 +122,7 @@ class SignalTest(common.HeatTestCase):
|
||||
|
||||
rsrc = self.stack['signal_handler']
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
rsrc._create_keypair()
|
||||
|
||||
# Ensure the resource data has been stored correctly
|
||||
rs_data = resource_data_object.ResourceData.get_all(rsrc)
|
||||
@ -174,8 +200,131 @@ class SignalTest(common.HeatTestCase):
|
||||
rsrc = self.stack['signal_handler']
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
|
||||
first_url = rsrc.FnGetAtt('AlarmUrl')
|
||||
second_url = rsrc.FnGetAtt('AlarmUrl')
|
||||
first_url = rsrc.FnGetAtt('signal')
|
||||
second_url = rsrc.FnGetAtt('signal')
|
||||
self.assertEqual(first_url, second_url)
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_FnGetAtt_Heat_Signal(self):
|
||||
self.stack = self.create_stack(test_heat_template_signal)
|
||||
self.m.StubOutWithMock(self.stack.clients.client_plugin('heat'),
|
||||
'get_heat_url')
|
||||
|
||||
self.stack.clients.client_plugin('heat').get_heat_url().AndReturn(
|
||||
'http://server.test:8004/v1')
|
||||
|
||||
self.m.ReplayAll()
|
||||
self.stack.create()
|
||||
|
||||
rsrc = self.stack['signal_handler']
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
|
||||
signal = rsrc.FnGetAtt('signal')
|
||||
self.assertEqual('http://localhost:5000/v3', signal['auth_url'])
|
||||
self.assertEqual('aprojectid', signal['project_id'])
|
||||
self.assertEqual('1234', signal['user_id'])
|
||||
self.assertIn('username', signal)
|
||||
self.assertIn('password', signal)
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_FnGetAtt_Heat_Signal_is_cached(self):
|
||||
self.stack = self.create_stack(test_heat_template_signal)
|
||||
|
||||
self.m.ReplayAll()
|
||||
self.stack.create()
|
||||
|
||||
rsrc = self.stack['signal_handler']
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
|
||||
first_url = rsrc.FnGetAtt('signal')
|
||||
second_url = rsrc.FnGetAtt('signal')
|
||||
self.assertEqual(first_url, second_url)
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_FnGetAtt_Zaqar_Signal(self):
|
||||
self.stack = self.create_stack(test_zaqar_template_signal)
|
||||
|
||||
self.m.ReplayAll()
|
||||
self.stack.create()
|
||||
|
||||
rsrc = self.stack['signal_handler']
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
|
||||
signal = rsrc.FnGetAtt('signal')
|
||||
self.assertEqual('http://localhost:5000/v3', signal['auth_url'])
|
||||
self.assertEqual('aprojectid', signal['project_id'])
|
||||
self.assertEqual('1234', signal['user_id'])
|
||||
self.assertIn('username', signal)
|
||||
self.assertIn('password', signal)
|
||||
self.assertIn('queue_id', signal)
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_FnGetAtt_Zaqar_Signal_is_cached(self):
|
||||
self.stack = self.create_stack(test_zaqar_template_signal)
|
||||
|
||||
self.m.ReplayAll()
|
||||
self.stack.create()
|
||||
|
||||
rsrc = self.stack['signal_handler']
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
|
||||
first_url = rsrc.FnGetAtt('signal')
|
||||
second_url = rsrc.FnGetAtt('signal')
|
||||
self.assertEqual(first_url, second_url)
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_FnGetAtt_Swift_Signal(self):
|
||||
self.stack = self.create_stack(test_swift_template_signal)
|
||||
self.m.StubOutWithMock(self.stack.clients.client('swift'),
|
||||
'put_container')
|
||||
self.m.StubOutWithMock(self.stack.clients.client('swift'),
|
||||
'put_object')
|
||||
self.m.StubOutWithMock(self.stack.clients.client_plugin('swift'),
|
||||
'get_temp_url')
|
||||
|
||||
self.stack.clients.client('swift').put_container(
|
||||
mox.IgnoreArg()).AndReturn(None)
|
||||
self.stack.clients.client_plugin('swift').get_temp_url(
|
||||
mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
|
||||
'http://192.0.2.1/v1/AUTH_aprojectid/foo/bar')
|
||||
self.stack.clients.client('swift').put_object(
|
||||
mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(None)
|
||||
|
||||
self.m.ReplayAll()
|
||||
self.stack.create()
|
||||
|
||||
rsrc = self.stack['signal_handler']
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
|
||||
self.assertEqual('http://192.0.2.1/v1/AUTH_aprojectid/foo/bar',
|
||||
rsrc.FnGetAtt('AlarmUrl'))
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_FnGetAtt_Swift_Signal_is_cached(self):
|
||||
self.stack = self.create_stack(test_swift_template_signal)
|
||||
self.m.StubOutWithMock(self.stack.clients.client('swift'),
|
||||
'put_container')
|
||||
self.m.StubOutWithMock(self.stack.clients.client('swift'),
|
||||
'put_object')
|
||||
self.m.StubOutWithMock(self.stack.clients.client_plugin('swift'),
|
||||
'get_temp_url')
|
||||
|
||||
self.stack.clients.client('swift').put_container(
|
||||
mox.IgnoreArg()).AndReturn(None)
|
||||
self.stack.clients.client_plugin('swift').get_temp_url(
|
||||
mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(
|
||||
'http://192.0.2.1/v1/AUTH_aprojectid/foo/' + uuid.uuid4().hex)
|
||||
self.stack.clients.client('swift').put_object(
|
||||
mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(None)
|
||||
|
||||
self.m.ReplayAll()
|
||||
self.stack.create()
|
||||
|
||||
rsrc = self.stack['signal_handler']
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
|
||||
first_url = rsrc.FnGetAtt('signal')
|
||||
second_url = rsrc.FnGetAtt('signal')
|
||||
self.assertEqual(first_url, second_url)
|
||||
self.m.VerifyAll()
|
||||
|
||||
@ -190,6 +339,7 @@ class SignalTest(common.HeatTestCase):
|
||||
self.stack.create()
|
||||
|
||||
rsrc = self.stack['signal_handler']
|
||||
rsrc.resource_id_set('signal')
|
||||
self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state)
|
||||
|
||||
self.assertIn('http://server.test:8000/v1/signal',
|
||||
|
@ -979,22 +979,22 @@ class SoftwareDeploymentTest(common.HeatTestCase):
|
||||
self.deployment.id = 23
|
||||
self.deployment.uuid = str(uuid.uuid4())
|
||||
self.deployment.action = self.deployment.CREATE
|
||||
container = self.deployment.physical_resource_name()
|
||||
object_name = self.deployment.physical_resource_name()
|
||||
|
||||
temp_url = self.deployment._get_temp_url()
|
||||
temp_url = self.deployment._get_swift_signal_url()
|
||||
temp_url_pattern = re.compile(
|
||||
'^http://192.0.2.1/v1/AUTH_test_tenant_id/'
|
||||
'(software_deployment_test_stack-deployment_mysql-.*)/(.*)'
|
||||
'(.*)/(software_deployment_test_stack-deployment_mysql-.*)'
|
||||
'\\?temp_url_sig=.*&temp_url_expires=\\d*$')
|
||||
self.assertRegex(temp_url, temp_url_pattern)
|
||||
m = temp_url_pattern.search(temp_url)
|
||||
object_name = m.group(2)
|
||||
self.assertEqual(container, m.group(1))
|
||||
self.assertEqual(dep_data['signal_object_name'], object_name)
|
||||
container = m.group(1)
|
||||
self.assertEqual(object_name, m.group(2))
|
||||
self.assertEqual(dep_data['swift_signal_object_name'], object_name)
|
||||
|
||||
self.assertEqual(dep_data['signal_temp_url'], temp_url)
|
||||
self.assertEqual(dep_data['swift_signal_url'], temp_url)
|
||||
|
||||
self.assertEqual(temp_url, self.deployment._get_temp_url())
|
||||
self.assertEqual(temp_url, self.deployment._get_swift_signal_url())
|
||||
|
||||
sc.put_container.assert_called_once_with(container)
|
||||
sc.put_object.assert_called_once_with(container, object_name, '')
|
||||
@ -1002,7 +1002,7 @@ class SoftwareDeploymentTest(common.HeatTestCase):
|
||||
def test_delete_temp_url(self):
|
||||
object_name = str(uuid.uuid4())
|
||||
dep_data = {
|
||||
'signal_object_name': object_name
|
||||
'swift_signal_object_name': object_name
|
||||
}
|
||||
self._create_stack(self.template_temp_url_signal)
|
||||
|
||||
@ -1021,31 +1021,34 @@ class SoftwareDeploymentTest(common.HeatTestCase):
|
||||
self.deployment.id = 23
|
||||
self.deployment.uuid = str(uuid.uuid4())
|
||||
container = self.deployment.physical_resource_name()
|
||||
self.deployment._delete_temp_url()
|
||||
self.deployment._delete_swift_signal_url()
|
||||
sc.delete_object.assert_called_once_with(container, object_name)
|
||||
self.assertEqual(
|
||||
[mock.call('signal_object_name'), mock.call('signal_temp_url')],
|
||||
[mock.call('swift_signal_object_name'),
|
||||
mock.call('swift_signal_url')],
|
||||
self.deployment.data_delete.mock_calls)
|
||||
|
||||
swift_exc = swift.SwiftClientPlugin.exceptions_module
|
||||
sc.delete_object.side_effect = swift_exc.ClientException(
|
||||
'Not found', http_status=404)
|
||||
self.deployment._delete_temp_url()
|
||||
self.deployment._delete_swift_signal_url()
|
||||
self.assertEqual(
|
||||
[mock.call('signal_object_name'), mock.call('signal_temp_url'),
|
||||
mock.call('signal_object_name'), mock.call('signal_temp_url')],
|
||||
[mock.call('swift_signal_object_name'),
|
||||
mock.call('swift_signal_url'),
|
||||
mock.call('swift_signal_object_name'),
|
||||
mock.call('swift_signal_url')],
|
||||
self.deployment.data_delete.mock_calls)
|
||||
|
||||
del(dep_data['signal_object_name'])
|
||||
del(dep_data['swift_signal_object_name'])
|
||||
self.deployment.physical_resource_name = mock.Mock()
|
||||
self.deployment._delete_temp_url()
|
||||
self.deployment._delete_swift_signal_url()
|
||||
self.assertFalse(self.deployment.physical_resource_name.called)
|
||||
|
||||
def test_handle_action_temp_url(self):
|
||||
|
||||
self._create_stack(self.template_temp_url_signal)
|
||||
dep_data = {
|
||||
'signal_temp_url': (
|
||||
'swift_signal_url': (
|
||||
'http://192.0.2.1/v1/AUTH_a/b/c'
|
||||
'?temp_url_sig=ctemp_url_expires=1234')
|
||||
}
|
||||
@ -1079,17 +1082,18 @@ class SoftwareDeploymentTest(common.HeatTestCase):
|
||||
self.deployment.uuid = str(uuid.uuid4())
|
||||
self.deployment.action = self.deployment.CREATE
|
||||
|
||||
queue_id = self.deployment._get_queue_id()
|
||||
queue_id = self.deployment._get_zaqar_signal_queue_id()
|
||||
self.assertEqual(2, len(zc.queue.mock_calls))
|
||||
self.assertEqual(queue_id, zc.queue.mock_calls[0][1][0])
|
||||
self.assertEqual(queue_id, dep_data['signal_queue_id'])
|
||||
self.assertEqual(queue_id, dep_data['zaqar_signal_queue_id'])
|
||||
|
||||
self.assertEqual(queue_id, self.deployment._get_queue_id())
|
||||
self.assertEqual(queue_id,
|
||||
self.deployment._get_zaqar_signal_queue_id())
|
||||
|
||||
def test_delete_zaqar_queue(self):
|
||||
queue_id = str(uuid.uuid4())
|
||||
dep_data = {
|
||||
'signal_queue_id': queue_id
|
||||
'zaqar_signal_queue_id': queue_id
|
||||
}
|
||||
self._create_stack(self.template_zaqar_signal)
|
||||
|
||||
@ -1103,23 +1107,24 @@ class SoftwareDeploymentTest(common.HeatTestCase):
|
||||
|
||||
self.deployment.id = 23
|
||||
self.deployment.uuid = str(uuid.uuid4())
|
||||
self.deployment._delete_queue()
|
||||
self.deployment._delete_zaqar_signal_queue()
|
||||
zc.queue.assert_called_once_with(queue_id)
|
||||
self.assertTrue(zc.queue(self.deployment.uuid).delete.called)
|
||||
self.assertEqual(
|
||||
[mock.call('signal_queue_id')],
|
||||
[mock.call('zaqar_signal_queue_id')],
|
||||
self.deployment.data_delete.mock_calls)
|
||||
|
||||
zaqar_exc = zaqar.ZaqarClientPlugin.exceptions_module
|
||||
zc.queue.delete.side_effect = zaqar_exc.ResourceNotFound()
|
||||
self.deployment._delete_queue()
|
||||
self.deployment._delete_zaqar_signal_queue()
|
||||
self.assertEqual(
|
||||
[mock.call('signal_queue_id'), mock.call('signal_queue_id')],
|
||||
[mock.call('zaqar_signal_queue_id'),
|
||||
mock.call('zaqar_signal_queue_id')],
|
||||
self.deployment.data_delete.mock_calls)
|
||||
|
||||
dep_data.pop('signal_queue_id')
|
||||
dep_data.pop('zaqar_signal_queue_id')
|
||||
self.deployment.physical_resource_name = mock.Mock()
|
||||
self.deployment._delete_queue()
|
||||
self.deployment._delete_zaqar_signal_queue()
|
||||
self.assertEqual(2, len(self.deployment.data_delete.mock_calls))
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user