Add OS::Swift::Signal resource
blueprint swiftsignal-resource Change-Id: I5ba523210aa9aefbf46eeaa0d95293608a820f95
This commit is contained in:
parent
f388b2e2a8
commit
4285dcc2de
319
heat/engine/resources/swiftsignal.py
Normal file
319
heat/engine/resources/swiftsignal.py
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import json
|
||||||
|
import urlparse
|
||||||
|
|
||||||
|
from swiftclient import client as swiftclient_client
|
||||||
|
|
||||||
|
from heat.common import exception
|
||||||
|
from heat.engine import attributes
|
||||||
|
from heat.engine.clients.os import swift
|
||||||
|
from heat.engine import constraints
|
||||||
|
from heat.engine import properties
|
||||||
|
from heat.engine import resource
|
||||||
|
from heat.engine import scheduler
|
||||||
|
from heat.openstack.common.gettextutils import _
|
||||||
|
from heat.openstack.common import log as logging
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class SwiftSignalFailure(exception.Error):
|
||||||
|
def __init__(self, wait_cond):
|
||||||
|
reasons = wait_cond.get_status_reason(wait_cond.STATUS_FAILURE)
|
||||||
|
super(SwiftSignalFailure, self).__init__(';'.join(reasons))
|
||||||
|
|
||||||
|
|
||||||
|
class SwiftSignalTimeout(exception.Error):
|
||||||
|
def __init__(self, wait_cond):
|
||||||
|
reasons = wait_cond.get_status_reason(wait_cond.STATUS_SUCCESS)
|
||||||
|
vals = {'len': len(reasons),
|
||||||
|
'count': wait_cond.properties[wait_cond.COUNT]}
|
||||||
|
if reasons:
|
||||||
|
vals['reasons'] = ';'.join(reasons)
|
||||||
|
message = (_('%(len)d of %(count)d received - %(reasons)s') % vals)
|
||||||
|
else:
|
||||||
|
message = (_('%(len)d of %(count)d received') % vals)
|
||||||
|
super(SwiftSignalTimeout, self).__init__(message)
|
||||||
|
|
||||||
|
|
||||||
|
class SwiftSignalHandle(resource.Resource):
|
||||||
|
|
||||||
|
properties_schema = {}
|
||||||
|
|
||||||
|
ATTRIBUTES = (
|
||||||
|
TOKEN,
|
||||||
|
ENDPOINT,
|
||||||
|
CURL_CLI,
|
||||||
|
) = (
|
||||||
|
'token',
|
||||||
|
'endpoint',
|
||||||
|
'curl_cli',
|
||||||
|
)
|
||||||
|
|
||||||
|
attributes_schema = {
|
||||||
|
TOKEN: attributes.Schema(
|
||||||
|
_('Tokens are not needed for Swift TempURLs. This attribute is '
|
||||||
|
'being kept for compatibility with the '
|
||||||
|
'OS::Heat::WaitConditionHandle resource'),
|
||||||
|
cache_mode=attributes.Schema.CACHE_NONE
|
||||||
|
),
|
||||||
|
ENDPOINT: attributes.Schema(
|
||||||
|
_('Endpoint/url which can be used for signalling handle'),
|
||||||
|
cache_mode=attributes.Schema.CACHE_NONE
|
||||||
|
),
|
||||||
|
CURL_CLI: attributes.Schema(
|
||||||
|
_('Convenience attribute, provides curl CLI command '
|
||||||
|
'prefix, which can be used for signalling handle completion or '
|
||||||
|
'failure. You can signal success by adding '
|
||||||
|
'--data-binary \'{"status": "SUCCESS"}\' '
|
||||||
|
', or signal failure by adding '
|
||||||
|
'--data-binary \'{"status": "FAILURE"}\''),
|
||||||
|
cache_mode=attributes.Schema.CACHE_NONE
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
def handle_create(self):
|
||||||
|
sc = self.client_plugin('swift')
|
||||||
|
url = sc.get_signal_url(self.stack.id, self.physical_resource_name())
|
||||||
|
self.data_set('endpoint', url)
|
||||||
|
self.resource_id_set(url)
|
||||||
|
|
||||||
|
def update(self, after, before=None, prev_resource=None):
|
||||||
|
raise resource.UpdateReplace(self.name)
|
||||||
|
|
||||||
|
def _resolve_attribute(self, key):
|
||||||
|
if self.resource_id:
|
||||||
|
if key == self.TOKEN:
|
||||||
|
return '' # HeatWaitConditionHandle compatibility
|
||||||
|
elif key == self.ENDPOINT:
|
||||||
|
return self.data().get('endpoint')
|
||||||
|
elif key == self.CURL_CLI:
|
||||||
|
return ('curl -i -X PUT \'%s\'' % self.data().get('endpoint'))
|
||||||
|
|
||||||
|
|
||||||
|
class SwiftSignal(resource.Resource):
|
||||||
|
|
||||||
|
PROPERTIES = (HANDLE, TIMEOUT, COUNT,) = ('handle', 'timeout', 'count',)
|
||||||
|
|
||||||
|
properties_schema = {
|
||||||
|
HANDLE: properties.Schema(
|
||||||
|
properties.Schema.STRING,
|
||||||
|
required=True,
|
||||||
|
description=_('URL of TempURL where resource will signal '
|
||||||
|
'completion and optionally upload data.')
|
||||||
|
),
|
||||||
|
TIMEOUT: properties.Schema(
|
||||||
|
properties.Schema.NUMBER,
|
||||||
|
description=_('The maximum number of seconds to wait for the '
|
||||||
|
'resource to signal completion. Once the timeout '
|
||||||
|
'is reached, creation of the signal resource will '
|
||||||
|
'fail.'),
|
||||||
|
required=True,
|
||||||
|
constraints=[
|
||||||
|
constraints.Range(1, 43200),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
COUNT: properties.Schema(
|
||||||
|
properties.Schema.NUMBER,
|
||||||
|
description=_('The number of success signals that must be '
|
||||||
|
'received before the stack creation process '
|
||||||
|
'continues.'),
|
||||||
|
default=1,
|
||||||
|
constraints=[
|
||||||
|
constraints.Range(1, 1000),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ATTRIBUTES = (DATA) = 'data'
|
||||||
|
|
||||||
|
attributes_schema = {
|
||||||
|
DATA: attributes.Schema(
|
||||||
|
_('JSON data that was uploaded via the SwiftSignalHandle.')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
WAIT_STATUSES = (
|
||||||
|
STATUS_FAILURE,
|
||||||
|
STATUS_SUCCESS,
|
||||||
|
) = (
|
||||||
|
'FAILURE',
|
||||||
|
'SUCCESS',
|
||||||
|
)
|
||||||
|
|
||||||
|
METADATA_KEYS = (
|
||||||
|
DATA, REASON, STATUS, UNIQUE_ID
|
||||||
|
) = (
|
||||||
|
'data', 'reason', 'status', 'id'
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, name, json_snippet, stack):
|
||||||
|
super(SwiftSignal, self).__init__(name, json_snippet, stack)
|
||||||
|
self._obj_name = None
|
||||||
|
self._url = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def url(self):
|
||||||
|
if not self._url:
|
||||||
|
self._url = urlparse.urlparse(self.properties[self.HANDLE])
|
||||||
|
return self._url
|
||||||
|
|
||||||
|
@property
|
||||||
|
def obj_name(self):
|
||||||
|
if not self._obj_name:
|
||||||
|
self._obj_name = self.url.path.split('/')[4]
|
||||||
|
return self._obj_name
|
||||||
|
|
||||||
|
def _validate_handle_url(self):
|
||||||
|
parts = self.url.path.split('/')
|
||||||
|
msg = _('"%(url)s" is not a valid SwiftSignalHandle. The %(part)s '
|
||||||
|
'is invalid')
|
||||||
|
sc = self.client_plugin('swift')
|
||||||
|
if not sc.is_valid_temp_url_path(self.url.path):
|
||||||
|
raise ValueError(msg % {'url': self.url.path,
|
||||||
|
'part': 'Swift TempURL path'})
|
||||||
|
if not parts[2].endswith(self.context.tenant_id):
|
||||||
|
raise ValueError(msg % {'url': self.url.path,
|
||||||
|
'part': 'tenant'})
|
||||||
|
if not parts[3] == self.stack.id:
|
||||||
|
raise ValueError(msg % {'url': self.url.path,
|
||||||
|
'part': 'container name'})
|
||||||
|
|
||||||
|
def handle_create(self):
|
||||||
|
self._validate_handle_url()
|
||||||
|
runner = scheduler.TaskRunner(self._wait)
|
||||||
|
runner.start(timeout=float(self.properties.get(self.TIMEOUT)))
|
||||||
|
return runner
|
||||||
|
|
||||||
|
def _wait(self):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
except scheduler.Timeout:
|
||||||
|
count = self.properties.get(self.COUNT)
|
||||||
|
raise SwiftSignalTimeout(self)
|
||||||
|
|
||||||
|
count = self.properties.get(self.COUNT)
|
||||||
|
statuses = self.get_status()
|
||||||
|
if not statuses:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for status in statuses:
|
||||||
|
if status == self.STATUS_FAILURE:
|
||||||
|
failure = SwiftSignalFailure(self)
|
||||||
|
LOG.info(_('%(name)s Failed (%(failure)s)')
|
||||||
|
% {'name': str(self), 'failure': str(failure)})
|
||||||
|
raise failure
|
||||||
|
elif status != self.STATUS_SUCCESS:
|
||||||
|
raise exception.Error(_("Unknown status: %s") % status)
|
||||||
|
|
||||||
|
if len(statuses) >= count:
|
||||||
|
LOG.info(_("%s Succeeded") % str(self))
|
||||||
|
return
|
||||||
|
|
||||||
|
def get_signals(self):
|
||||||
|
try:
|
||||||
|
container = self.swift().get_container(self.stack.id)
|
||||||
|
except swiftclient_client.ClientException as exc:
|
||||||
|
if exc.http_status == 404: # Swift container was deleted by user
|
||||||
|
return None
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
index = container[1]
|
||||||
|
if not index: # Swift objects were deleted by user
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Remove objects in that are for other handle resources, since
|
||||||
|
# multiple SwiftSignalHandle resources in the same stack share
|
||||||
|
# a container
|
||||||
|
filtered = [obj for obj in index if self.obj_name in obj['name']]
|
||||||
|
|
||||||
|
# Fetch objects from Swift and filter results
|
||||||
|
obj_bodies = []
|
||||||
|
for obj in filtered:
|
||||||
|
try:
|
||||||
|
signal = self.swift().get_object(self.stack.id, obj['name'])
|
||||||
|
except swiftclient_client.ClientException as exc:
|
||||||
|
if exc.http_status == 404: # Swift object disappeared
|
||||||
|
continue
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
body = signal[1]
|
||||||
|
if body == swift.IN_PROGRESS: # Ignore the initial object
|
||||||
|
continue
|
||||||
|
if body == "":
|
||||||
|
obj_bodies.append({})
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
obj_bodies.append(json.loads(body))
|
||||||
|
except ValueError:
|
||||||
|
raise exception.Error(_("Failed to parse JSON data: %s") %
|
||||||
|
body)
|
||||||
|
|
||||||
|
# Set default values on each signal
|
||||||
|
signals = []
|
||||||
|
signal_num = 1
|
||||||
|
for signal in obj_bodies:
|
||||||
|
|
||||||
|
# Remove previous signals with the same ID
|
||||||
|
id = self.UNIQUE_ID
|
||||||
|
ids = [s.get(id) for s in signals if id in s]
|
||||||
|
if ids and id in signal and ids.count(signal[id]) > 0:
|
||||||
|
[signals.remove(s) for s in signals if s.get(id) == signal[id]]
|
||||||
|
|
||||||
|
# Make sure all fields are set, since all are optional
|
||||||
|
signal.setdefault(self.DATA, None)
|
||||||
|
unique_id = signal.setdefault(self.UNIQUE_ID, signal_num)
|
||||||
|
reason = 'Signal %s recieved' % unique_id
|
||||||
|
signal.setdefault(self.REASON, reason)
|
||||||
|
signal.setdefault(self.STATUS, self.STATUS_SUCCESS)
|
||||||
|
|
||||||
|
signals.append(signal)
|
||||||
|
signal_num += 1
|
||||||
|
|
||||||
|
return signals
|
||||||
|
|
||||||
|
def get_status(self):
|
||||||
|
return [s[self.STATUS] for s in self.get_signals()]
|
||||||
|
|
||||||
|
def get_status_reason(self, status):
|
||||||
|
return [s[self.REASON]
|
||||||
|
for s in self.get_signals()
|
||||||
|
if s[self.STATUS] == status]
|
||||||
|
|
||||||
|
def get_data(self):
|
||||||
|
signals = self.get_signals()
|
||||||
|
if not signals:
|
||||||
|
return None
|
||||||
|
data = {}
|
||||||
|
for signal in signals:
|
||||||
|
data[signal[self.UNIQUE_ID]] = signal[self.DATA]
|
||||||
|
return data
|
||||||
|
|
||||||
|
def check_create_complete(self, runner):
|
||||||
|
return runner.step()
|
||||||
|
|
||||||
|
def _resolve_attribute(self, key):
|
||||||
|
if key == self.DATA:
|
||||||
|
return unicode(json.dumps(self.get_data()))
|
||||||
|
|
||||||
|
|
||||||
|
def resource_mapping():
|
||||||
|
return {'OS::Heat::SwiftSignal': SwiftSignal,
|
||||||
|
'OS::Heat::SwiftSignalHandle': SwiftSignalHandle}
|
||||||
|
|
||||||
|
|
||||||
|
def available_resource_mapping():
|
||||||
|
return resource_mapping()
|
756
heat/tests/test_swiftsignal.py
Normal file
756
heat/tests/test_swiftsignal.py
Normal file
@ -0,0 +1,756 @@
|
|||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
import copy
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import mock
|
||||||
|
import six
|
||||||
|
from swiftclient import client as swiftclient_client
|
||||||
|
from testtools.matchers import MatchesRegex
|
||||||
|
|
||||||
|
from heat.common import template_format
|
||||||
|
from heat.engine.clients.os import swift
|
||||||
|
from heat.engine import environment
|
||||||
|
from heat.engine import resource
|
||||||
|
from heat.engine import rsrc_defn
|
||||||
|
from heat.engine import scheduler
|
||||||
|
from heat.engine import stack
|
||||||
|
from heat.tests.common import HeatTestCase
|
||||||
|
from heat.tests import utils
|
||||||
|
|
||||||
|
|
||||||
|
swiftsignal_template = '''
|
||||||
|
heat_template_version: 2013-05-23
|
||||||
|
|
||||||
|
resources:
|
||||||
|
test_wait_condition:
|
||||||
|
type: "OS::Heat::SwiftSignal"
|
||||||
|
properties:
|
||||||
|
handle: { get_resource: test_wait_condition_handle }
|
||||||
|
timeout: 1
|
||||||
|
count: 2
|
||||||
|
|
||||||
|
test_wait_condition_handle:
|
||||||
|
type: "OS::Heat::SwiftSignalHandle"
|
||||||
|
'''
|
||||||
|
|
||||||
|
swiftsignalhandle_template = '''
|
||||||
|
heat_template_version: 2013-05-23
|
||||||
|
|
||||||
|
resources:
|
||||||
|
test_wait_condition_handle:
|
||||||
|
type: "OS::Heat::SwiftSignalHandle"
|
||||||
|
'''
|
||||||
|
|
||||||
|
container_header = {
|
||||||
|
'content-length': '2',
|
||||||
|
'x-container-object-count': '0',
|
||||||
|
'accept-ranges': 'bytes',
|
||||||
|
'date': 'Fri, 25 Jul 2014 16:02:03 GMT',
|
||||||
|
'x-timestamp': '1405019787.66969',
|
||||||
|
'x-trans-id': 'tx6651b005324341f685e71-0053d27f7bdfw1',
|
||||||
|
'x-container-bytes-used': '0',
|
||||||
|
'content-type': 'application/json; charset=utf-8',
|
||||||
|
'x-versions-location': 'test'
|
||||||
|
}
|
||||||
|
|
||||||
|
obj_header = {
|
||||||
|
'content-length': '5',
|
||||||
|
'accept-ranges': 'bytes',
|
||||||
|
'last-modified': 'Fri, 25 Jul 2014 16:05:26 GMT',
|
||||||
|
'etag': '5a105e8b9d40e1329780d62ea2265d8a',
|
||||||
|
'x-timestamp': '1406304325.40094',
|
||||||
|
'x-trans-id': 'tx2f40ff2b4daa4015917fc-0053d28045dfw1',
|
||||||
|
'date': 'Fri, 25 Jul 2014 16:05:25 GMT',
|
||||||
|
'content-type': 'application/octet-stream'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def create_stack(template, stack_id=None):
|
||||||
|
tmpl = template_format.parse(template)
|
||||||
|
template = stack.Template(tmpl)
|
||||||
|
ctx = utils.dummy_context(tenant_id='test_tenant')
|
||||||
|
st = stack.Stack(ctx, 'test_st', template,
|
||||||
|
environment.Environment(),
|
||||||
|
disable_rollback=True)
|
||||||
|
|
||||||
|
# Stub out the stack ID so we have a known value
|
||||||
|
if stack_id is None:
|
||||||
|
stack_id = str(uuid.uuid4())
|
||||||
|
with utils.UUIDStub(stack_id):
|
||||||
|
st.store()
|
||||||
|
st.id = stack_id
|
||||||
|
|
||||||
|
return st
|
||||||
|
|
||||||
|
|
||||||
|
def cont_index(obj_name, num_version_hist):
|
||||||
|
objects = [{'bytes': 11,
|
||||||
|
'last_modified': '2014-07-03T19:42:03.281640',
|
||||||
|
'hash': '9214b4e4460fcdb9f3a369941400e71e',
|
||||||
|
'name': "02b" + obj_name + '/1404416326.51383',
|
||||||
|
'content_type': 'application/octet-stream'}] * num_version_hist
|
||||||
|
objects.append({'bytes': 8,
|
||||||
|
'last_modified': '2014-07-03T19:42:03.849870',
|
||||||
|
'hash': '9ab7c0738852d7dd6a2dc0b261edc300',
|
||||||
|
'name': obj_name,
|
||||||
|
'content_type': 'application/x-www-form-urlencoded'})
|
||||||
|
return (container_header, objects)
|
||||||
|
|
||||||
|
|
||||||
|
class SwiftSignalHandleTest(HeatTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(SwiftSignalHandleTest, self).setUp()
|
||||||
|
utils.setup_dummy_db()
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_create(self, mock_name, mock_swift):
|
||||||
|
st = create_stack(swiftsignalhandle_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': "1234"
|
||||||
|
}
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.return_value = cont_index(obj_name, 2)
|
||||||
|
mock_swift_object.get_object.return_value = (obj_header, '{"id": "1"}')
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
handle = st.resources['test_wait_condition_handle']
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
regexp = ("http://fake-host.com:8080/v1/AUTH_test_tenant/%s/test_st-"
|
||||||
|
"test_wait_condition_handle-abcdefghijkl"
|
||||||
|
"\?temp_url_sig=[0-9a-f]{40}&temp_url_expires=[0-9]{10}"
|
||||||
|
% st.id)
|
||||||
|
res_id = st.resources['test_wait_condition_handle'].resource_id
|
||||||
|
self.assertThat(res_id, MatchesRegex(regexp))
|
||||||
|
|
||||||
|
# Since the account key is mocked out above
|
||||||
|
self.assertFalse(mock_swift_object.post_account.called)
|
||||||
|
|
||||||
|
header = {'x-versions-location': st.id}
|
||||||
|
self.assertEqual({'headers': header},
|
||||||
|
mock_swift_object.put_container.call_args[1])
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
def test_handle_update(self, mock_swift):
|
||||||
|
st = create_stack(swiftsignalhandle_template)
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.head_account.return_value = {}
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
uprops = copy.copy(handle.properties.data)
|
||||||
|
uprops['count'] = '5'
|
||||||
|
update_snippet = rsrc_defn.ResourceDefinition(handle.name,
|
||||||
|
handle.type(),
|
||||||
|
uprops)
|
||||||
|
|
||||||
|
updater = scheduler.TaskRunner(handle.update, update_snippet)
|
||||||
|
self.assertRaises(resource.UpdateReplace, updater)
|
||||||
|
|
||||||
|
|
||||||
|
class SwiftSignalTest(HeatTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(SwiftSignalTest, self).setUp()
|
||||||
|
utils.setup_dummy_db()
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_create(self, mock_name, mock_swift):
|
||||||
|
st = create_stack(swiftsignal_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': '123456'
|
||||||
|
}
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.return_value = cont_index(obj_name, 2)
|
||||||
|
mock_swift_object.get_object.return_value = (obj_header, '')
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertEqual(('CREATE', 'COMPLETE'), st.state)
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, 'get_signal_url')
|
||||||
|
def test_validate_handle_url_bad_tempurl(self, mock_handle_url):
|
||||||
|
mock_handle_url.return_value = (
|
||||||
|
"http://fake-host.com:8080/v1/my-container/"
|
||||||
|
"test_st-test_wait_condition_handle?temp_url_sig="
|
||||||
|
"12d8f9f2c923fbeb555041d4ed63d83de6768e95&"
|
||||||
|
"temp_url_expires=1404762741")
|
||||||
|
st = create_stack(swiftsignal_template)
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertIn('not a valid SwiftSignalHandle. The Swift TempURL path',
|
||||||
|
six.text_type(st.status_reason))
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, 'get_signal_url')
|
||||||
|
def test_validate_handle_url_bad_container_name(self, mock_handle_url):
|
||||||
|
mock_handle_url.return_value = (
|
||||||
|
"http://fake-host.com:8080/v1/AUTH_test_tenant/my-container/"
|
||||||
|
"test_st-test_wait_condition_handle?temp_url_sig="
|
||||||
|
"12d8f9f2c923fbeb555041d4ed63d83de6768e95&"
|
||||||
|
"temp_url_expires=1404762741")
|
||||||
|
st = create_stack(swiftsignal_template)
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertIn('not a valid SwiftSignalHandle. The container name',
|
||||||
|
six.text_type(st.status_reason))
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, 'get_signal_url')
|
||||||
|
def test_validate_handle_url_bad_tenant(self, mock_handle_url):
|
||||||
|
stack_id = '1234'
|
||||||
|
mock_handle_url.return_value = (
|
||||||
|
"http://fake-host.com:8080/v1/AUTH_foo/%s/"
|
||||||
|
"test_st-test_wait_condition_handle?temp_url_sig="
|
||||||
|
"12d8f9f2c923fbeb555041d4ed63d83de6768e95&"
|
||||||
|
"temp_url_expires=1404762741" % stack_id)
|
||||||
|
st = create_stack(swiftsignal_template, stack_id=stack_id)
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertIn('not a valid SwiftSignalHandle. The tenant',
|
||||||
|
six.text_type(st.status_reason))
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_multiple_signals_same_id_complete(self, mock_name, mock_swift):
|
||||||
|
st = create_stack(swiftsignal_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': '123456'
|
||||||
|
}
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.return_value = cont_index(obj_name, 2)
|
||||||
|
mock_swift_object.get_object.side_effect = (
|
||||||
|
(obj_header, json.dumps({'id': 1})),
|
||||||
|
(obj_header, json.dumps({'id': 1})),
|
||||||
|
(obj_header, json.dumps({'id': 1})),
|
||||||
|
|
||||||
|
(obj_header, json.dumps({'id': 1})),
|
||||||
|
(obj_header, json.dumps({'id': 2})),
|
||||||
|
(obj_header, json.dumps({'id': 3})),
|
||||||
|
)
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertEqual(('CREATE', 'COMPLETE'), st.state)
|
||||||
|
|
||||||
|
@mock.patch.object(scheduler, 'wallclock')
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_multiple_signals_same_id_timeout(self, mock_name, mock_swift,
|
||||||
|
mock_clock):
|
||||||
|
st = create_stack(swiftsignal_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': '123456'
|
||||||
|
}
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.return_value = cont_index(obj_name, 2)
|
||||||
|
mock_swift_object.get_object.return_value = (obj_header,
|
||||||
|
json.dumps({'id': 1}))
|
||||||
|
|
||||||
|
time_now = time.time()
|
||||||
|
time_series = [t + time_now for t in xrange(1, 100)]
|
||||||
|
scheduler.wallclock.side_effect = time_series
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertIn("Resource CREATE failed: SwiftSignalTimeout",
|
||||||
|
st.status_reason)
|
||||||
|
wc = st['test_wait_condition']
|
||||||
|
self.assertEqual("SwiftSignalTimeout: 1 of 2 received - Signal 1 "
|
||||||
|
"recieved", wc.status_reason)
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_post_complete_to_handle(self, mock_name, mock_swift):
|
||||||
|
st = create_stack(swiftsignal_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': '123456'
|
||||||
|
}
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.return_value = cont_index(obj_name, 2)
|
||||||
|
mock_swift_object.get_object.side_effect = (
|
||||||
|
(obj_header, json.dumps({'id': 1, 'status': "SUCCESS"})),
|
||||||
|
(obj_header, json.dumps({'id': 1, 'status': "SUCCESS"})),
|
||||||
|
(obj_header, json.dumps({'id': 2, 'status': "SUCCESS"})),
|
||||||
|
)
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertEqual(('CREATE', 'COMPLETE'), st.state)
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_post_failed_to_handle(self, mock_name, mock_swift):
|
||||||
|
st = create_stack(swiftsignal_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': '123456'
|
||||||
|
}
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.return_value = cont_index(obj_name, 1)
|
||||||
|
mock_swift_object.get_object.side_effect = (
|
||||||
|
# Create
|
||||||
|
(obj_header, json.dumps({'id': 1, 'status': "FAILURE",
|
||||||
|
'reason': "foo"})),
|
||||||
|
(obj_header, json.dumps({'id': 2, 'status': "FAILURE",
|
||||||
|
'reason': "bar"})),
|
||||||
|
|
||||||
|
# SwiftSignalFailure
|
||||||
|
(obj_header, json.dumps({'id': 1, 'status': "FAILURE",
|
||||||
|
'reason': "foo"})),
|
||||||
|
(obj_header, json.dumps({'id': 2, 'status': "FAILURE",
|
||||||
|
'reason': "bar"})),
|
||||||
|
)
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertEqual(('CREATE', 'FAILED'), st.state)
|
||||||
|
wc = st['test_wait_condition']
|
||||||
|
self.assertEqual("SwiftSignalFailure: foo;bar", wc.status_reason)
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_data(self, mock_name, mock_swift):
|
||||||
|
st = create_stack(swiftsignal_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': '123456'
|
||||||
|
}
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.return_value = cont_index(obj_name, 2)
|
||||||
|
|
||||||
|
mock_swift_object.get_object.side_effect = (
|
||||||
|
# st create
|
||||||
|
(obj_header, json.dumps({'id': 1, 'data': "foo"})),
|
||||||
|
(obj_header, json.dumps({'id': 2, 'data': "bar"})),
|
||||||
|
(obj_header, json.dumps({'id': 3, 'data': "baz"})),
|
||||||
|
|
||||||
|
# FnGetAtt call
|
||||||
|
(obj_header, json.dumps({'id': 1, 'data': "foo"})),
|
||||||
|
(obj_header, json.dumps({'id': 2, 'data': "bar"})),
|
||||||
|
(obj_header, json.dumps({'id': 3, 'data': "baz"})),
|
||||||
|
)
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertEqual(('CREATE', 'COMPLETE'), st.state)
|
||||||
|
wc = st['test_wait_condition']
|
||||||
|
self.assertEqual(json.dumps({1: 'foo', 2: 'bar', 3: 'baz'}),
|
||||||
|
wc.FnGetAtt('data'))
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_data_noid(self, mock_name, mock_swift):
|
||||||
|
st = create_stack(swiftsignal_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': '123456'
|
||||||
|
}
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.return_value = cont_index(obj_name, 1)
|
||||||
|
|
||||||
|
mock_swift_object.get_object.side_effect = (
|
||||||
|
# st create
|
||||||
|
(obj_header, json.dumps({'data': "foo", 'reason': "bar",
|
||||||
|
'status': "SUCCESS"})),
|
||||||
|
(obj_header, json.dumps({'data': "dog", 'reason': "cat",
|
||||||
|
'status': "SUCCESS"})),
|
||||||
|
|
||||||
|
# FnGetAtt call
|
||||||
|
(obj_header, json.dumps({'data': "foo", 'reason': "bar",
|
||||||
|
'status': "SUCCESS"})),
|
||||||
|
(obj_header, json.dumps({'data': "dog", 'reason': "cat",
|
||||||
|
'status': "SUCCESS"})),
|
||||||
|
)
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertEqual(('CREATE', 'COMPLETE'), st.state)
|
||||||
|
wc = st['test_wait_condition']
|
||||||
|
self.assertEqual(json.dumps({1: 'foo', 2: 'dog'}), wc.FnGetAtt('data'))
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_data_nodata(self, mock_name, mock_swift):
|
||||||
|
st = create_stack(swiftsignal_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': '123456'
|
||||||
|
}
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.return_value = cont_index(obj_name, 1)
|
||||||
|
|
||||||
|
mock_swift_object.get_object.side_effect = (
|
||||||
|
# st create
|
||||||
|
(obj_header, ''),
|
||||||
|
(obj_header, ''),
|
||||||
|
|
||||||
|
# FnGetAtt call
|
||||||
|
(obj_header, ''),
|
||||||
|
(obj_header, ''),
|
||||||
|
)
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertEqual(('CREATE', 'COMPLETE'), st.state)
|
||||||
|
wc = st['test_wait_condition']
|
||||||
|
self.assertEqual(json.dumps({1: None, 2: None}), wc.FnGetAtt('data'))
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_data_partial_complete(self, mock_name, mock_swift):
|
||||||
|
st = create_stack(swiftsignal_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
wc = st['test_wait_condition']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': '123456'
|
||||||
|
}
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.return_value = cont_index(obj_name, 1)
|
||||||
|
mock_swift_object.get_object.return_value = (
|
||||||
|
obj_header, json.dumps({'status': 'SUCCESS'}))
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertEqual(['SUCCESS', 'SUCCESS'], wc.get_status())
|
||||||
|
expected = [{'status': 'SUCCESS', 'reason': 'Signal 1 recieved',
|
||||||
|
'data': None, 'id': 1},
|
||||||
|
{'status': 'SUCCESS', 'reason': 'Signal 2 recieved',
|
||||||
|
'data': None, 'id': 2}]
|
||||||
|
self.assertEqual(expected, wc.get_signals())
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_get_status_none_complete(self, mock_name, mock_swift):
|
||||||
|
st = create_stack(swiftsignal_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
wc = st['test_wait_condition']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': '123456'
|
||||||
|
}
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.return_value = cont_index(obj_name, 1)
|
||||||
|
mock_swift_object.get_object.return_value = (obj_header, '')
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertEqual(['SUCCESS', 'SUCCESS'], wc.get_status())
|
||||||
|
expected = [{'status': 'SUCCESS', 'reason': 'Signal 1 recieved',
|
||||||
|
'data': None, 'id': 1},
|
||||||
|
{'status': 'SUCCESS', 'reason': 'Signal 2 recieved',
|
||||||
|
'data': None, 'id': 2}]
|
||||||
|
self.assertEqual(expected, wc.get_signals())
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_get_status_partial_complete(self, mock_name, mock_swift):
|
||||||
|
st = create_stack(swiftsignal_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
wc = st['test_wait_condition']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': '123456'
|
||||||
|
}
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.return_value = cont_index(obj_name, 1)
|
||||||
|
mock_swift_object.get_object.return_value = (
|
||||||
|
obj_header, json.dumps({'id': 1, 'status': "SUCCESS"}))
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertEqual(['SUCCESS'], wc.get_status())
|
||||||
|
expected = [{'status': 'SUCCESS', 'reason': 'Signal 1 recieved',
|
||||||
|
'data': None, 'id': 1}]
|
||||||
|
self.assertEqual(expected, wc.get_signals())
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_get_status_failure(self, mock_name, mock_swift):
|
||||||
|
st = create_stack(swiftsignal_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
wc = st['test_wait_condition']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': '123456'
|
||||||
|
}
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.return_value = cont_index(obj_name, 1)
|
||||||
|
mock_swift_object.get_object.return_value = (
|
||||||
|
obj_header, json.dumps({'id': 1, 'status': "FAILURE"}))
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertEqual(('CREATE', 'FAILED'), st.state)
|
||||||
|
self.assertEqual(['FAILURE'], wc.get_status())
|
||||||
|
expected = [{'status': 'FAILURE', 'reason': 'Signal 1 recieved',
|
||||||
|
'data': None, 'id': 1}]
|
||||||
|
self.assertEqual(expected, wc.get_signals())
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_getatt_token(self, mock_name, mock_swift):
|
||||||
|
st = create_stack(swiftsignalhandle_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': '123456'
|
||||||
|
}
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.return_value = cont_index(obj_name, 1)
|
||||||
|
|
||||||
|
mock_swift_object.get_object.side_effect = (
|
||||||
|
# st create
|
||||||
|
(obj_header, ''),
|
||||||
|
(obj_header, ''),
|
||||||
|
)
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertEqual(('CREATE', 'COMPLETE'), st.state)
|
||||||
|
self.assertEqual(handle.FnGetAtt('token'), '')
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_getatt_endpoint(self, mock_name, mock_swift):
|
||||||
|
st = create_stack(swiftsignalhandle_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': '123456'
|
||||||
|
}
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.return_value = cont_index(obj_name, 1)
|
||||||
|
|
||||||
|
mock_swift_object.get_object.side_effect = (
|
||||||
|
# st create
|
||||||
|
(obj_header, ''),
|
||||||
|
(obj_header, ''),
|
||||||
|
)
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertEqual(('CREATE', 'COMPLETE'), st.state)
|
||||||
|
expected = ('http://fake-host.com:8080/v1/AUTH_test_tenant/%s/'
|
||||||
|
'test_st-test_wait_condition_handle-abcdefghijkl\?temp_'
|
||||||
|
'url_sig=[0-9a-f]{40}&temp_url_expires=[0-9]{10}') % st.id
|
||||||
|
self.assertThat(handle.FnGetAtt('endpoint'),
|
||||||
|
MatchesRegex(expected))
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_getatt_curl_cli(self, mock_name, mock_swift):
|
||||||
|
st = create_stack(swiftsignalhandle_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': '123456'
|
||||||
|
}
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.return_value = cont_index(obj_name, 1)
|
||||||
|
|
||||||
|
mock_swift_object.get_object.side_effect = (
|
||||||
|
# st create
|
||||||
|
(obj_header, ''),
|
||||||
|
(obj_header, ''),
|
||||||
|
)
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertEqual(('CREATE', 'COMPLETE'), st.state)
|
||||||
|
expected = ('curl -i -X PUT \'http://fake-host.com:8080/v1/'
|
||||||
|
'AUTH_test_tenant/%s/test_st-test_wait_condition_'
|
||||||
|
'handle-abcdefghijkl\?temp_url_sig=[0-9a-f]{40}&'
|
||||||
|
'temp_url_expires=[0-9]{10}\'') % st.id
|
||||||
|
self.assertThat(handle.FnGetAtt('curl_cli'), MatchesRegex(expected))
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_invalid_json_data(self, mock_name, mock_swift):
|
||||||
|
st = create_stack(swiftsignal_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': '123456'
|
||||||
|
}
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.return_value = cont_index(obj_name, 1)
|
||||||
|
|
||||||
|
mock_swift_object.get_object.side_effect = (
|
||||||
|
# st create
|
||||||
|
(obj_header, '{"status": "SUCCESS"'),
|
||||||
|
(obj_header, '{"status": "FAI'),
|
||||||
|
)
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertEqual(('CREATE', 'FAILED'), st.state)
|
||||||
|
wc = st['test_wait_condition']
|
||||||
|
self.assertEqual('Error: Failed to parse JSON data: {"status": '
|
||||||
|
'"SUCCESS"', wc.status_reason)
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_unknown_status(self, mock_name, mock_swift):
|
||||||
|
st = create_stack(swiftsignal_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': '123456'
|
||||||
|
}
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.return_value = cont_index(obj_name, 1)
|
||||||
|
|
||||||
|
mock_swift_object.get_object.return_value = (
|
||||||
|
obj_header, '{"status": "BOO"}')
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertEqual(('CREATE', 'FAILED'), st.state)
|
||||||
|
wc = st['test_wait_condition']
|
||||||
|
self.assertEqual('Error: Unknown status: BOO', wc.status_reason)
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_swift_objects_deleted(self, mock_name, mock_swift):
|
||||||
|
st = create_stack(swiftsignal_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': '123456'
|
||||||
|
}
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.side_effect = (
|
||||||
|
cont_index(obj_name, 2), # Objects are there during create
|
||||||
|
(container_header, []), # The user deleted the objects
|
||||||
|
)
|
||||||
|
mock_swift_object.get_object.side_effect = (
|
||||||
|
(obj_header, json.dumps({'id': 1})), # Objects there during create
|
||||||
|
(obj_header, json.dumps({'id': 2})),
|
||||||
|
(obj_header, json.dumps({'id': 3})),
|
||||||
|
)
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertEqual(('CREATE', 'COMPLETE'), st.state)
|
||||||
|
wc = st['test_wait_condition']
|
||||||
|
self.assertEqual("null", wc.FnGetAtt('data'))
|
||||||
|
|
||||||
|
@mock.patch.object(swift.SwiftClientPlugin, '_create')
|
||||||
|
@mock.patch.object(resource.Resource, 'physical_resource_name')
|
||||||
|
def test_swift_container_deleted(self, mock_name, mock_swift):
|
||||||
|
st = create_stack(swiftsignal_template)
|
||||||
|
handle = st['test_wait_condition_handle']
|
||||||
|
|
||||||
|
mock_swift_object = mock.Mock()
|
||||||
|
mock_swift.return_value = mock_swift_object
|
||||||
|
mock_swift_object.url = "http://fake-host.com:8080/v1/AUTH_1234"
|
||||||
|
mock_swift_object.head_account.return_value = {
|
||||||
|
'x-account-meta-temp-url-key': '123456'
|
||||||
|
}
|
||||||
|
obj_name = "%s-%s-abcdefghijkl" % (st.name, handle.name)
|
||||||
|
mock_name.return_value = obj_name
|
||||||
|
mock_swift_object.get_container.side_effect = [
|
||||||
|
cont_index(obj_name, 2), # Objects are there during create
|
||||||
|
swiftclient_client.ClientException("Container GET failed",
|
||||||
|
http_status=404) # User deleted
|
||||||
|
]
|
||||||
|
mock_swift_object.get_object.side_effect = (
|
||||||
|
(obj_header, json.dumps({'id': 1})), # Objects there during create
|
||||||
|
(obj_header, json.dumps({'id': 2})),
|
||||||
|
(obj_header, json.dumps({'id': 3})),
|
||||||
|
)
|
||||||
|
|
||||||
|
st.create()
|
||||||
|
self.assertEqual(('CREATE', 'COMPLETE'), st.state)
|
||||||
|
wc = st['test_wait_condition']
|
||||||
|
self.assertEqual("null", wc.FnGetAtt('data'))
|
Loading…
Reference in New Issue
Block a user