Remove message v1 support

This has been marked for deprecation for 9 months and the v1 message
REST api has been deprecated since 2014.

This also updates the message version resource to Resource2.

Change-Id: I623297060fda2c7f44c08f5842d308efb62d4247
This commit is contained in:
Monty Taylor 2018-01-12 12:52:37 -06:00
parent cb4c425411
commit 536f347a6c
No known key found for this signature in database
GPG Key ID: 7BAE94BC7141A594
17 changed files with 9 additions and 657 deletions

View File

@ -37,7 +37,6 @@ def get_proxy_methods():
"openstack.image.v2._proxy",
"openstack.key_manager.v1._proxy",
"openstack.load_balancer.v2._proxy",
"openstack.message.v1._proxy",
"openstack.message.v2._proxy",
"openstack.network.v2._proxy",
"openstack.object_store.v1._proxy",

View File

@ -91,7 +91,6 @@ provided by the SDK.
Image v2 <proxies/image_v2>
Key Manager <proxies/key_manager>
Load Balancer <proxies/load_balancer_v2>
Message v1 <proxies/message_v1>
Message v2 <proxies/message_v2>
Network <proxies/network>
Object Store <proxies/object_store>

View File

@ -1,30 +0,0 @@
Message API v1
==============
For details on how to use message, see :doc:`/user/guides/message`
.. automodule:: openstack.message.v1._proxy
The Message v1 Class
--------------------
The message high-level interface is available through the ``message`` member
of a :class:`~openstack.connection.Connection` object. The ``message``
member will only be added if the service is detected.
Message Operations
^^^^^^^^^^^^^^^^^^
.. autoclass:: openstack.message.v1._proxy.Proxy
.. automethod:: openstack.message.v1._proxy.Proxy.claim_messages
.. automethod:: openstack.message.v1._proxy.Proxy.create_messages
.. automethod:: openstack.message.v1._proxy.Proxy.delete_message
Queue Operations
^^^^^^^^^^^^^^^^
.. autoclass:: openstack.message.v1._proxy.Proxy
.. automethod:: openstack.message.v1._proxy.Proxy.create_queue
.. automethod:: openstack.message.v1._proxy.Proxy.delete_queue

View File

@ -16,8 +16,7 @@ from openstack import service_filter
class MessageService(service_filter.ServiceFilter):
"""The message service."""
valid_versions = [service_filter.ValidVersion('v1'),
service_filter.ValidVersion('v2')]
valid_versions = [service_filter.ValidVersion('v2')]
def __init__(self, version=None):
"""Create a message service."""

View File

@ -1,93 +0,0 @@
# 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.
from openstack.message.v1 import claim
from openstack.message.v1 import message
from openstack.message.v1 import queue
from openstack import proxy
from openstack import utils
class Proxy(proxy.BaseProxy):
@utils.deprecated(deprecated_in="0.9.16", removed_in="0.9.17",
details="Message v1 is deprecated since 2014. Use v2.")
def create_queue(self, **attrs):
"""Create a new queue from attributes
:param dict attrs: Keyword arguments which will be used to create
a :class:`~openstack.message.v1.queue.Queue`,
comprised of the properties on the Queue class.
:returns: The results of queue creation
:rtype: :class:`~openstack.message.v1.queue.Queue`
"""
return self._create(queue.Queue, **attrs)
@utils.deprecated(deprecated_in="0.9.16", removed_in="0.9.17",
details="Message v1 is deprecated since 2014. Use v2.")
def delete_queue(self, value, ignore_missing=True):
"""Delete a queue
:param value: The value can be either the name of a queue or a
:class:`~openstack.message.v1.queue.Queue` instance.
:param bool ignore_missing: When set to ``False``
:class:`~openstack.exceptions.ResourceNotFound` will be
raised when the queue does not exist.
When set to ``True``, no exception will be set when
attempting to delete a nonexistent queue.
:returns: ``None``
"""
return self._delete(queue.Queue, value, ignore_missing=ignore_missing)
@utils.deprecated(deprecated_in="0.9.16", removed_in="0.9.17",
details="Message v1 is deprecated since 2014. Use v2.")
def create_messages(self, values):
"""Create new messages
:param values: The list of
:class:`~openstack.message.v1.message.Message` objects
to create.
:type values: :py:class:`list`
:returns: The list of
:class:`~openstack.message.v1.message.Message` objects
that were created.
"""
return message.Message.create_messages(self, values)
@utils.deprecated(deprecated_in="0.9.16", removed_in="0.9.17",
details="Message v1 is deprecated since 2014. Use v2.")
def claim_messages(self, value):
"""Claims a set of messages.
:param value: The value must be a
:class:`~openstack.message.v1.claim.Claim` instance.
:returns: The list of
:class:`~openstack.message.v1.message.Message` objects
that were claimed.
"""
return claim.Claim.claim_messages(self, value)
@utils.deprecated(deprecated_in="0.9.16", removed_in="0.9.17",
details="Message v1 is deprecated since 2014. Use v2.")
def delete_message(self, value):
"""Delete a message
:param value: The value must be a
:class:`~openstack.message.v1.message.Message` instance.
:returns: ``None``
"""
message.Message.delete_by_id(self, value)

View File

@ -1,87 +0,0 @@
# 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
from openstack import exceptions
from openstack.message import message_service
from openstack.message.v1 import message
from openstack import resource
class Claim(resource.Resource):
resources_key = 'claims'
base_path = "/queues/%(queue_name)s/claims"
service = message_service.MessageService()
# capabilities
allow_create = True
allow_list = False
allow_retrieve = False
allow_delete = False
#: A ID for each client instance. The ID must be submitted in its
#: canonical form (for example, 3381af92-2b9e-11e3-b191-71861300734c).
#: The client generates this ID once. The client ID persists between
#: restarts of the client so the client should reuse that same ID.
#: All message-related operations require the use of the client ID in
#: the headers to ensure that messages are not echoed back to the client
#: that posted them, unless the client explicitly requests this.
client_id = None
#: The name of the queue this Claim belongs to.
queue_name = None
#: Specifies the number of Messages to return.
limit = None
#: Specifies how long the server waits before releasing the claim,
#: in seconds.
ttl = resource.prop("ttl")
#: Specifies the message grace period, in seconds.
grace = resource.prop("grace")
@classmethod
def claim_messages(cls, session, claim):
"""Create a remote resource from this instance."""
url = cls._get_url({'queue_name': claim.queue_name})
headers = {'Client-ID': claim.client_id}
params = {'limit': claim.limit} if claim.limit else None
body = []
try:
resp = session.post(url,
headers=headers,
data=json.dumps(claim, cls=ClaimEncoder),
params=params)
body = resp.json()
except exceptions.InvalidResponse as e:
# The Message Service will respond with a 204 and no content in
# the body when there are no messages to claim. The transport
# layer doesn't like that and we have to correct for it here.
# Ultimately it's a bug in the v1.0 Message Service API.
# TODO(etoews): API is fixed in v1.1 so fix this for message.v1_1
# https://wiki.openstack.org/wiki/Zaqar/specs/api/v1.1
if e.response.status_code != 204:
raise e
for message_attrs in body:
yield message.Message.new(
client_id=claim.client_id,
queue_name=claim.queue_name,
**message_attrs)
class ClaimEncoder(json.JSONEncoder):
def default(self, claim):
return {'ttl': claim.ttl, 'grace': claim.grace}

View File

@ -1,107 +0,0 @@
# 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
from six.moves.urllib import parse
from openstack.message import message_service
from openstack import resource
class Message(resource.Resource):
resources_key = 'messages'
base_path = "/queues/%(queue_name)s/messages"
service = message_service.MessageService()
# capabilities
allow_create = True
allow_list = False
allow_retrieve = False
allow_delete = False
#: A ID for each client instance. The ID must be submitted in its
#: canonical form (for example, 3381af92-2b9e-11e3-b191-71861300734c).
#: The client generates this ID once. The client ID persists between
#: restarts of the client so the client should reuse that same ID.
#: All message-related operations require the use of the client ID in
#: the headers to ensure that messages are not echoed back to the client
#: that posted them, unless the client explicitly requests this.
client_id = None
#: The name of the queue this Message belongs to.
queue_name = None
#: A relative href that references this Message.
href = resource.prop("href")
#: An arbitrary JSON document that constitutes the body of the message
#: being sent.
body = resource.prop("body")
#: Specifies how long the server waits, in seconds, before marking the
#: message as expired and removing it from the queue.
ttl = resource.prop("ttl")
#: Specifies how long the message has been in the queue, in seconds.
age = resource.prop("age")
@classmethod
def create_messages(cls, session, messages):
if len(messages) == 0:
raise ValueError('messages cannot be empty')
for i, message in enumerate(messages, -1):
if message.queue_name != messages[i].queue_name:
raise ValueError('All queues in messages must be equal')
if message.client_id != messages[i].client_id:
raise ValueError('All clients in messages must be equal')
url = cls._get_url({'queue_name': messages[0].queue_name})
headers = {'Client-ID': messages[0].client_id}
resp = session.post(url, headers=headers,
data=json.dumps(messages, cls=MessageEncoder))
resp = resp.json()
messages_created = []
hrefs = resp['resources']
for i, href in enumerate(hrefs):
message = Message.existing(**messages[i])
message.href = href
messages_created.append(message)
return messages_created
@classmethod
def _strip_version(cls, href):
path = parse.urlparse(href).path
if path.startswith('/v'):
return href[href.find('/', 1):]
else:
return href
@classmethod
def delete_by_id(cls, session, message, path_args=None):
url = cls._strip_version(message.href)
headers = {
'Client-ID': message.client_id,
'Accept': '',
}
session.delete(url, headers=headers)
class MessageEncoder(json.JSONEncoder):
def default(self, message):
return {'body': message.body, 'ttl': message.ttl}

View File

@ -1,34 +0,0 @@
# 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.
from openstack.message import message_service
from openstack import resource
class Queue(resource.Resource):
id_attribute = 'name'
resources_key = 'queues'
base_path = '/queues'
service = message_service.MessageService()
# capabilities
allow_create = True
allow_list = False
allow_retrieve = False
allow_delete = True
@classmethod
def create_by_id(cls, session, attrs, resource_id=None, path_args=None):
url = cls._get_url(path_args, resource_id)
headers = {'Accept': ''}
session.put(url, headers=headers)
return {cls.id_attribute: resource_id}

View File

@ -11,7 +11,7 @@
# under the License.
from openstack.message import message_service
from openstack import resource
from openstack import resource2 as resource
class Version(resource.Resource):
@ -26,5 +26,5 @@ class Version(resource.Resource):
allow_list = True
# Properties
links = resource.prop('links')
status = resource.prop('status')
links = resource.Body('links')
status = resource.Body('status')

View File

@ -23,8 +23,6 @@ class TestMessageService(testtools.TestCase):
self.assertEqual('public', sot.interface)
self.assertIsNone(sot.region)
self.assertIsNone(sot.service_name)
self.assertEqual(2, len(sot.valid_versions))
self.assertEqual('v1', sot.valid_versions[0].module)
self.assertEqual('v1', sot.valid_versions[0].path)
self.assertEqual('v2', sot.valid_versions[1].module)
self.assertEqual('v2', sot.valid_versions[1].path)
self.assertEqual(1, len(sot.valid_versions))
self.assertEqual('v2', sot.valid_versions[0].module)
self.assertEqual('v2', sot.valid_versions[0].path)

View File

@ -31,13 +31,13 @@ class TestVersion(testtools.TestCase):
self.assertEqual('/', sot.base_path)
self.assertEqual('messaging', sot.service.service_type)
self.assertFalse(sot.allow_create)
self.assertFalse(sot.allow_retrieve)
self.assertFalse(sot.allow_get)
self.assertFalse(sot.allow_update)
self.assertFalse(sot.allow_delete)
self.assertTrue(sot.allow_list)
def test_make_it(self):
sot = version.Version(EXAMPLE)
sot = version.Version(**EXAMPLE)
self.assertEqual(EXAMPLE['id'], sot.id)
self.assertEqual(EXAMPLE['links'], sot.links)
self.assertEqual(EXAMPLE['status'], sot.status)

View File

@ -1,97 +0,0 @@
# 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 mock
import testtools
from openstack import exceptions
from openstack.message.v1 import claim
CLIENT = '3381af92-2b9e-11e3-b191-71861300734c'
QUEUE = 'test_queue'
LIMIT = 2
FAKE = {
'ttl': 300,
'grace': 60
}
class TestClaim(testtools.TestCase):
def test_basic(self):
sot = claim.Claim()
self.assertEqual('claims', sot.resources_key)
self.assertEqual('/queues/%(queue_name)s/claims', sot.base_path)
self.assertEqual('messaging', sot.service.service_type)
self.assertTrue(sot.allow_create)
self.assertFalse(sot.allow_retrieve)
self.assertFalse(sot.allow_update)
self.assertFalse(sot.allow_delete)
self.assertFalse(sot.allow_list)
def test_make_it(self):
sot = claim.Claim.new(client_id=CLIENT,
queue_name=QUEUE,
limit=LIMIT,
**FAKE)
self.assertEqual(CLIENT, sot.client_id)
self.assertEqual(QUEUE, sot.queue_name)
self.assertEqual(LIMIT, sot.limit)
self.assertEqual(FAKE['ttl'], sot.ttl)
self.assertEqual(FAKE['grace'], sot.grace)
def test_create(self):
sess = mock.Mock()
obj = mock.Mock()
fake_attrs = [{'foo': 'bar'}, {'zoo': 'lah'}]
obj.json = mock.Mock(return_value=fake_attrs)
sess.post = mock.Mock(return_value=obj)
sot = claim.Claim()
c = claim.Claim.new(client_id=CLIENT, queue_name=QUEUE, **FAKE)
list(sot.claim_messages(sess, c))
url = '/queues/%s/claims' % QUEUE
sess.post.assert_called_with(
url,
headers={'Client-ID': CLIENT}, params=None,
data=json.dumps(FAKE, cls=claim.ClaimEncoder))
def test_claim_messages_no_invalid_response(self):
sess = mock.Mock()
resp = mock.Mock()
resp.status_code = 204
sess.post = mock.Mock(
side_effect=exceptions.InvalidResponse(response=resp))
sot = claim.Claim()
messages = list(sot.claim_messages(
sess, claim.Claim.new(client_id=CLIENT, queue_name=QUEUE, **FAKE)))
self.assertEqual(0, len(messages))
def test_claim_messages_invalid_response(self):
sess = mock.Mock()
resp = mock.Mock()
resp.status_code = 400
sess.post = mock.Mock(
side_effect=exceptions.InvalidResponse(response=resp))
sot = claim.Claim()
try:
list(sot.claim_messages(
sess, claim.Claim.new(client_id=CLIENT,
queue_name=QUEUE,
**FAKE)))
except exceptions.InvalidResponse as e:
self.assertEqual(400, e.response.status_code)

View File

@ -1,85 +0,0 @@
# 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 mock
import testtools
from openstack.message.v1 import message
CLIENT = '3381af92-2b9e-11e3-b191-71861300734c'
QUEUE = 'test_queue'
FAKE = {
'ttl': 300,
'body': {'key': 'value'}
}
FAKE_HREF = {
'href': '/v1/queues/test_queue/messages/1234',
'ttl': 300,
'body': {'key': 'value'}
}
class TestMessage(testtools.TestCase):
def test_basic(self):
sot = message.Message()
self.assertEqual('messages', sot.resources_key)
self.assertEqual('/queues/%(queue_name)s/messages', sot.base_path)
self.assertEqual('messaging', sot.service.service_type)
self.assertTrue(sot.allow_create)
self.assertFalse(sot.allow_retrieve)
self.assertFalse(sot.allow_update)
self.assertFalse(sot.allow_delete)
self.assertFalse(sot.allow_list)
def test_make_it(self):
sot = message.Message(FAKE)
self.assertEqual(FAKE['ttl'], sot.ttl)
self.assertEqual(FAKE['body'], sot.body)
def test_create(self):
sess = mock.Mock()
obj = mock.Mock()
obj.json = mock.Mock(return_value={'resources': {'k': 'v'}})
sess.post = mock.Mock(return_value=obj)
sot = message.Message()
msg = message.Message.new(client_id=CLIENT, queue_name=QUEUE, **FAKE)
sot.create_messages(sess, [msg])
url = '/queues/%s/messages' % QUEUE
sess.post.assert_called_with(
url,
headers={'Client-ID': CLIENT},
data=mock.ANY)
args, kwargs = sess.post.call_args
self.assertIn("data", kwargs)
self.assertDictEqual(json.loads(kwargs["data"])[0], FAKE)
def test_delete(self):
sess = mock.Mock()
sess.delete = mock.Mock()
sess.delete.return_value = mock.Mock()
sot = message.Message()
sot.delete_by_id(
sess, message.Message.new(client_id=CLIENT,
queue_name=QUEUE,
**FAKE_HREF))
url = '/queues/%s/messages/1234' % QUEUE
sess.delete.assert_called_with(
url,
headers={'Client-ID': CLIENT, 'Accept': ''})

View File

@ -1,55 +0,0 @@
# 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.
from openstack.message.v1 import _proxy
from openstack.message.v1 import claim
from openstack.message.v1 import message
from openstack.message.v1 import queue
from openstack.tests.unit import test_proxy_base
CLIENT_ID = '3381af92-2b9e-11e3-b191-71861300734c'
QUEUE_NAME = 'test_queue'
class TestMessageProxy(test_proxy_base.TestProxyBase):
def setUp(self):
super(TestMessageProxy, self).setUp()
self.proxy = _proxy.Proxy(self.session)
def test_queue_create_attrs(self):
self.verify_create(self.proxy.create_queue, queue.Queue)
def test_queue_delete(self):
self.verify_delete(self.proxy.delete_queue, queue.Queue, False)
def test_queue_delete_ignore(self):
self.verify_delete(self.proxy.delete_queue, queue.Queue, True)
def test_messages_create(self):
self._verify2("openstack.message.v1.message.Message.create_messages",
self.proxy.create_messages,
expected_result="result",
method_args=[[]],
expected_args=[self.proxy, []])
def test_messages_claim(self):
self._verify2("openstack.message.v1.claim.Claim.claim_messages",
self.proxy.claim_messages,
expected_result="result",
method_args=[claim.Claim],
expected_args=[self.proxy, claim.Claim])
def test_message_delete(self):
self._verify2("openstack.message.v1.message.Message.delete_by_id",
self.proxy.delete_message,
method_args=[message.Message],
expected_args=[self.proxy, message.Message])

View File

@ -1,55 +0,0 @@
# 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 mock
import testtools
from openstack.message.v1 import queue
FAKE_NAME = 'test_queue'
FAKE = {
'name': FAKE_NAME,
}
class TestQueue(testtools.TestCase):
def test_basic(self):
sot = queue.Queue()
self.assertEqual('queues', sot.resources_key)
self.assertEqual('/queues', sot.base_path)
self.assertEqual('messaging', sot.service.service_type)
self.assertTrue(sot.allow_create)
self.assertFalse(sot.allow_retrieve)
self.assertFalse(sot.allow_update)
self.assertTrue(sot.allow_delete)
self.assertFalse(sot.allow_list)
def test_make_it(self):
sot = queue.Queue(FAKE)
self.assertEqual(FAKE['name'], sot.name)
def test_create(self):
sess = mock.Mock()
sess.put = mock.Mock()
sess.put.return_value = mock.Mock()
sot = queue.Queue(FAKE)
sot.create(sess)
url = 'queues/%s' % FAKE_NAME
headers = {'Accept': ''}
sess.put.assert_called_with(url,
headers=headers)
self.assertEqual(FAKE_NAME, sot.id)
self.assertEqual(FAKE_NAME, sot.name)