Move signals to SignalResponder class
This change relocates the code that creates and deletes heat, swift and zaqar signals out of SoftwareDeployment and into SignalResponder, as a first step in making these signals available to all resources. As part of this change the structure of the Swift URLs was changed to match that of the SwiftSignal resource. Change-Id: Id5701b3696b0ab41433a3f158fabce1f36aabe16 Implements: blueprint uniform-resource-signals
This commit is contained in:
parent
74fe413631
commit
ca8effbbae
heat
engine/resources
tests
@ -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
|
||||
|
@ -132,8 +132,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()
|
||||
@ -144,8 +147,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)
|
||||
zc.queue.delete.assert_called_once()
|
||||
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…
x
Reference in New Issue
Block a user