summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBarry Warsaw2010-02-16 17:17:38 -0500
committerBarry Warsaw2010-02-16 17:17:38 -0500
commitcf6e714bc3c68a8b9d788370fbdee4df6764e936 (patch)
tree47321a735879e40e50a228db254f6efbc3018a4b /src
parent5d12a07fccf0278293e222ca5a5c4a7653d4f0af (diff)
downloadmailman-cf6e714bc3c68a8b9d788370fbdee4df6764e936.tar.gz
mailman-cf6e714bc3c68a8b9d788370fbdee4df6764e936.tar.zst
mailman-cf6e714bc3c68a8b9d788370fbdee4df6764e936.zip
Oh FFS. lazr.restful 0.9.18 totally broke our shit by introducing
multiversioned web services. In concept, that's a great idea, but in (current as of 0.9.18) practice it sucks because it /forces/ us to adopt multiversions when really I could care less. After gobs of painful experimentation, this passes all the tests. I guess that means it's right <wink>. Another, more reasonable fix is to re-order when logging is initialized. This is moved to the start of initialize_2() because in the test environment, we don't want to initialize the loggers until after the test configuration has been pushed. Otherwise, because of the changes to support the FHS, logging will go to the wrong place. This really only affects tests, since in operational mode, initialize always happens immediately one after another.
Diffstat (limited to 'src')
-rw-r--r--src/mailman/core/initialize.py19
-rw-r--r--src/mailman/rest/configuration.py21
-rw-r--r--src/mailman/rest/configure.zcml14
-rw-r--r--src/mailman/rest/publication.py4
-rw-r--r--src/mailman/rest/urls.py8
-rw-r--r--src/mailman/rest/webservice.py82
6 files changed, 100 insertions, 48 deletions
diff --git a/src/mailman/core/initialize.py b/src/mailman/core/initialize.py
index 7c03a61db..e55e97ee5 100644
--- a/src/mailman/core/initialize.py
+++ b/src/mailman/core/initialize.py
@@ -87,19 +87,15 @@ def search_for_configuration_file():
# initialization, but before database initialization. Generally all other
# code will just call initialize().
-def initialize_1(config_path=None, propagate_logs=None):
+def initialize_1(config_path=None):
"""First initialization step.
* Zope component architecture
* The configuration system
* Run-time directories
- * The logging subsystem
- * Internationalization
:param config_path: The path to the configuration file.
:type config_path: string
- :param propagate_logs: Should the log output propagate to stderr?
- :type propagate_logs: boolean or None
"""
zcml = resource_string('mailman.config', 'configure.zcml')
xmlconfig.string(zcml)
@@ -119,13 +115,12 @@ def initialize_1(config_path=None, propagate_logs=None):
# For the test suite, force this back to not using a config file.
config_path = None
mailman.config.config.load(config_path)
- # Create the queue and log directories if they don't already exist.
- mailman.core.logging.initialize(propagate_logs)
-def initialize_2(debug=False):
+def initialize_2(debug=False, propagate_logs=None):
"""Second initialization step.
+ * Logging
* Pre-hook
* Rules
* Chains
@@ -134,7 +129,11 @@ def initialize_2(debug=False):
:param debug: Should the database layer be put in debug mode?
:type debug: boolean
+ :param propagate_logs: Should the log output propagate to stderr?
+ :type propagate_logs: boolean or None
"""
+ # Create the queue and log directories if they don't already exist.
+ mailman.core.logging.initialize(propagate_logs)
# Run the pre-hook if there is one.
config = mailman.config.config
if config.mailman.pre_hook:
@@ -172,6 +171,6 @@ def initialize_3():
def initialize(config_path=None, propagate_logs=None):
- initialize_1(config_path, propagate_logs)
- initialize_2()
+ initialize_1(config_path)
+ initialize_2(propagate_logs=propagate_logs)
initialize_3()
diff --git a/src/mailman/rest/configuration.py b/src/mailman/rest/configuration.py
index df756d76a..30e2607cb 100644
--- a/src/mailman/rest/configuration.py
+++ b/src/mailman/rest/configuration.py
@@ -55,18 +55,33 @@ class AdminWebServiceConfiguration(BaseWSGIWebServiceConfiguration):
"""See `IWebServiceConfiguration`."""
return as_boolean(config.webservice.use_https)
- # This should match the major.minor Mailman version.
- service_version_uri_prefix = '{0.MAJOR_REV}.{0.MINOR_REV}'.format(version)
+ # We currently have only one active version; the first entry in this list
+ # should match the major.minor Mailman version. The second entry is just
+ # an alias for the 'floating' development version.
+ active_versions = [
+ '{0.MAJOR_REV}.{0.MINOR_REV}'.format(version),
+ 'dev',
+ ]
code_revision = version.VERSION
@property
def show_tracebacks(self):
"""See `IWebServiceConfiguration`."""
return config.webservice.show_tracebacks
-
+
default_batch_size = 50
max_batch_size = 300
def get_request_user(self):
"""See `IWebServiceConfiguration`."""
return None
+
+ @property
+ def hostname(self):
+ """See `IWebServiceConfiguration`."""
+ return config.webservice.hostname
+
+ @property
+ def port(self):
+ """See `IWebServiceConfiguration`."""
+ return int(config.webservice.port)
diff --git a/src/mailman/rest/configure.zcml b/src/mailman/rest/configure.zcml
index fff1b5bac..7fecf4608 100644
--- a/src/mailman/rest/configure.zcml
+++ b/src/mailman/rest/configure.zcml
@@ -5,8 +5,7 @@
<include package="zope.component" file="meta.zcml"/>
<include package="zope.security" file="meta.zcml"/>
- <include package="lazr.restful" file="meta.zcml"/>
- <include package="lazr.restful" file="configure.zcml"/>
+ <include package="lazr.restful.example.wsgi" file="site.zcml"/>
<webservice:register module="mailman.interfaces.domain" />
<webservice:register module="mailman.interfaces.listmanager" />
@@ -36,6 +35,12 @@
/>
<adapter
+ for="mailman.interfaces.system.ISystem
+ lazr.restful.simple.Request"
+ provides="zope.traversing.browser.interfaces.IAbsoluteURL"
+ factory="mailman.rest.urls.FallbackURLMapper"
+ />
+ <adapter
for="mailman.interfaces.mailinglist.IMailingList
lazr.restful.simple.Request"
provides="zope.traversing.browser.interfaces.IAbsoluteURL"
@@ -83,6 +88,11 @@
<!-- Utilities -->
<utility
+ factory="mailman.rest.webservice.AdminWebServiceRootResource"
+ provides="lazr.restful.interfaces.IServiceRootResource"
+ />
+
+ <utility
factory="mailman.rest.configuration.AdminWebServiceConfiguration"
provides="lazr.restful.interfaces.IWebServiceConfiguration"
/>
diff --git a/src/mailman/rest/publication.py b/src/mailman/rest/publication.py
index 13866861f..becca8fa6 100644
--- a/src/mailman/rest/publication.py
+++ b/src/mailman/rest/publication.py
@@ -25,12 +25,16 @@ __all__ = [
]
+import logging
+
from lazr.restful.simple import Publication
from zope.publisher.interfaces import NotFound
from mailman.config import config
from mailman.interfaces.rest import IResolvePathNames
+log = logging.getLogger('mailman.http')
+
class AdminWebServicePublication(Publication):
diff --git a/src/mailman/rest/urls.py b/src/mailman/rest/urls.py
index ec8e40557..1d7adc0dd 100644
--- a/src/mailman/rest/urls.py
+++ b/src/mailman/rest/urls.py
@@ -22,7 +22,9 @@ from __future__ import absolute_import, unicode_literals
__metaclass__ = type
__all__ = [
'DomainURLMapper',
+ 'FallbackURLMapper',
'MailingListURLMapper',
+ 'MemberURLMapper',
]
@@ -53,10 +55,10 @@ class BasicURLMapper:
self.context = context
self.request = request
self.webservice_config = AdminWebServiceConfiguration()
- self.version = self.webservice_config.service_version_uri_prefix
+ self.version = self.webservice_config.active_versions[0]
self.schema = ('https' if self.webservice_config.use_https else 'http')
- self.hostname = config.webservice.hostname
- self.port = int(config.webservice.port)
+ self.hostname = self.webservice_config.hostname
+ self.port = self.webservice_config.port
diff --git a/src/mailman/rest/webservice.py b/src/mailman/rest/webservice.py
index 7f7cd898a..e30ac856d 100644
--- a/src/mailman/rest/webservice.py
+++ b/src/mailman/rest/webservice.py
@@ -34,15 +34,21 @@ import logging
# proper Mailman logger instead of stderr, as is the default.
from wsgiref.simple_server import WSGIServer, WSGIRequestHandler
-from lazr.restful.simple import Request
+from lazr.restful import register_versioned_request_utility
+from lazr.restful.interfaces import (
+ IServiceRootResource, IWebServiceClientRequest)
+from lazr.restful.simple import Request, RootResource
+from lazr.restful.wsgi import WSGIApplication
from zope.component import getUtility
from zope.interface import implements
from zope.publisher.publish import publish
from mailman.config import config
from mailman.core.system import system
-from mailman.interfaces.domain import IDomainCollection, IDomainManager
+from mailman.interfaces.domain import IDomain, IDomainCollection
from mailman.interfaces.listmanager import IListManager
+from mailman.interfaces.mailinglist import IMailingList
+from mailman.interfaces.member import IMember
from mailman.interfaces.membership import ISubscriptionService
from mailman.interfaces.rest import IResolvePathNames
from mailman.rest.publication import AdminWebServicePublication
@@ -51,46 +57,59 @@ log = logging.getLogger('mailman.http')
-class AdminWebServiceApplication:
- """A WSGI application for the admin REST interface."""
+# Marker interfaces for multiversion lazr.restful.
+class I30Version(IWebServiceClientRequest):
+ pass
+
+
+class IDevVersion(IWebServiceClientRequest):
+ pass
+
+
+
+class AdminWebServiceRootResource(RootResource):
+ """The lazr.restful non-versioned root resource."""
implements(IResolvePathNames)
- def __init__(self, environ, start_response):
- self.environ = environ
- self.start_response = start_response
+ def __init__(self):
+ # We can't build these mappings at construction time.
+ self._collections = None
+ self._entry_links = None
+ self._top_names = None
- def __iter__(self):
- environ = self.environ
- # Create the request based on the HTTP method used.
- method = environ.get('REQUEST_METHOD', 'GET').upper()
- request = Request(environ['wsgi.input'], environ)
- request.setPublication(AdminWebServicePublication(self))
- # Support post-mortem debugging.
- handle_errors = environ.get('wsgi.handleErrors', True)
- # The request returned by the publisher may in fact be different than
- # the one passed in.
- request = publish(request, handle_errors=handle_errors)
- # Start the WSGI server response.
- response = request.response
- self.start_response(response.getStatusString(), response.getHeaders())
- # Return the result body iterable.
- return iter(response.consumeBodyIter())
+ def _build_top_level_objects(self):
+ """See `RootResource`."""
+ self._collections = dict(
+ domains=(IDomain, getUtility(IDomainCollection)),
+ lists=(IMailingList, getUtility(IListManager)),
+ members=(IMember, getUtility(ISubscriptionService)),
+ )
+ self._entry_links = dict(
+ system=system,
+ )
+ self._top_names = self._collection.copy()
+ self._top_names.update(self._entry_links)
+ return (self._collections, self._entry_links)
def get(self, name):
- """Maps root names to resources."""
- top_level = dict(
- system=system,
+ """See `IResolvePathNames`."""
+ top_names = dict(
domains=getUtility(IDomainCollection),
lists=getUtility(IListManager),
members=getUtility(ISubscriptionService),
+ system=system,
)
- next_step = top_level.get(name)
- log.debug('Top level name: %s -> %s', name, next_step)
- return next_step
+ return top_names.get(name)
+
+
+class AdminWebServiceApplication(WSGIApplication):
+ """A WSGI application for the admin REST interface."""
+
+ # The only thing we need to override is the publication class.
+ publication_class = AdminWebServicePublication
-
class AdminWebServiceWSGIRequestHandler(WSGIRequestHandler):
"""Handler class which just logs output to the right place."""
@@ -99,8 +118,11 @@ class AdminWebServiceWSGIRequestHandler(WSGIRequestHandler):
log.info('%s - - %s', self.address_string(), format % args)
+
def make_server():
"""Create the WSGI admin REST server."""
+ register_versioned_request_utility(I30Version, '3.0')
+ register_versioned_request_utility(IDevVersion, 'dev')
host = config.webservice.hostname
port = int(config.webservice.port)
server = WSGIServer((host, port), AdminWebServiceWSGIRequestHandler)