Add pecan transport driver and tests
This commit is contained in:
parent
7ced7423a2
commit
1737ae877d
|
@ -0,0 +1,29 @@
|
||||||
|
# Copyright (c) 2014 Rackspace, Inc.
|
||||||
|
#
|
||||||
|
# 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 abc
|
||||||
|
import six
|
||||||
|
|
||||||
|
from cdn.manager.base import controller
|
||||||
|
|
||||||
|
|
||||||
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
|
class V1ControllerBase(controller.ManagerControllerBase):
|
||||||
|
def __init__(self, manager):
|
||||||
|
super(V1ControllerBase, self).__init__(manager)
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def get(self):
|
||||||
|
raise NotImplementedError
|
|
@ -0,0 +1,31 @@
|
||||||
|
from cdn.manager import base
|
||||||
|
|
||||||
|
JSON_HOME = {
|
||||||
|
"resources": {
|
||||||
|
"rel/cdn": {
|
||||||
|
"href-template": "services{?marker,limit}",
|
||||||
|
"href-vars": {
|
||||||
|
"marker": "param/marker",
|
||||||
|
"limit": "param/limit"
|
||||||
|
},
|
||||||
|
"hints": {
|
||||||
|
"allow": [
|
||||||
|
"GET"
|
||||||
|
],
|
||||||
|
"formats": {
|
||||||
|
"application/json": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultV1Controller(base.V1Controller):
|
||||||
|
def __init__(self, manager):
|
||||||
|
super(DefaultV1Controller, self).__init__(manager)
|
||||||
|
|
||||||
|
self.JSON_HOME = JSON_HOME
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
return self.JSON_HOME
|
|
@ -0,0 +1,126 @@
|
||||||
|
# Copyright 2011 OpenStack Foundation.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Simple class that stores security context information in the web request.
|
||||||
|
|
||||||
|
Projects should subclass this class if they wish to enhance the request
|
||||||
|
context or provide additional information in their specific WSGI pipeline.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import itertools
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
def generate_request_id():
|
||||||
|
return b'req-' + str(uuid.uuid4()).encode('ascii')
|
||||||
|
|
||||||
|
|
||||||
|
class RequestContext(object):
|
||||||
|
|
||||||
|
"""Helper class to represent useful information about a request context.
|
||||||
|
|
||||||
|
Stores information about the security context under which the user
|
||||||
|
accesses the system, as well as additional request information.
|
||||||
|
"""
|
||||||
|
|
||||||
|
user_idt_format = '{user} {tenant} {domain} {user_domain} {p_domain}'
|
||||||
|
|
||||||
|
def __init__(self, auth_token=None, user=None, tenant=None, domain=None,
|
||||||
|
user_domain=None, project_domain=None, is_admin=False,
|
||||||
|
read_only=False, show_deleted=False, request_id=None,
|
||||||
|
instance_uuid=None):
|
||||||
|
self.auth_token = auth_token
|
||||||
|
self.user = user
|
||||||
|
self.tenant = tenant
|
||||||
|
self.domain = domain
|
||||||
|
self.user_domain = user_domain
|
||||||
|
self.project_domain = project_domain
|
||||||
|
self.is_admin = is_admin
|
||||||
|
self.read_only = read_only
|
||||||
|
self.show_deleted = show_deleted
|
||||||
|
self.instance_uuid = instance_uuid
|
||||||
|
if not request_id:
|
||||||
|
request_id = generate_request_id()
|
||||||
|
self.request_id = request_id
|
||||||
|
|
||||||
|
def to_dict(self):
|
||||||
|
user_idt = (
|
||||||
|
self.user_idt_format.format(user=self.user or '-',
|
||||||
|
tenant=self.tenant or '-',
|
||||||
|
domain=self.domain or '-',
|
||||||
|
user_domain=self.user_domain or '-',
|
||||||
|
p_domain=self.project_domain or '-'))
|
||||||
|
|
||||||
|
return {'user': self.user,
|
||||||
|
'tenant': self.tenant,
|
||||||
|
'domain': self.domain,
|
||||||
|
'user_domain': self.user_domain,
|
||||||
|
'project_domain': self.project_domain,
|
||||||
|
'is_admin': self.is_admin,
|
||||||
|
'read_only': self.read_only,
|
||||||
|
'show_deleted': self.show_deleted,
|
||||||
|
'auth_token': self.auth_token,
|
||||||
|
'request_id': self.request_id,
|
||||||
|
'instance_uuid': self.instance_uuid,
|
||||||
|
'user_identity': user_idt}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, ctx):
|
||||||
|
return cls(
|
||||||
|
auth_token=ctx.get("auth_token"),
|
||||||
|
user=ctx.get("user"),
|
||||||
|
tenant=ctx.get("tenant"),
|
||||||
|
domain=ctx.get("domain"),
|
||||||
|
user_domain=ctx.get("user_domain"),
|
||||||
|
project_domain=ctx.get("project_domain"),
|
||||||
|
is_admin=ctx.get("is_admin", False),
|
||||||
|
read_only=ctx.get("read_only", False),
|
||||||
|
show_deleted=ctx.get("show_deleted", False),
|
||||||
|
request_id=ctx.get("request_id"),
|
||||||
|
instance_uuid=ctx.get("instance_uuid"))
|
||||||
|
|
||||||
|
|
||||||
|
def get_admin_context(show_deleted=False):
|
||||||
|
context = RequestContext(None,
|
||||||
|
tenant=None,
|
||||||
|
is_admin=True,
|
||||||
|
show_deleted=show_deleted)
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
def get_context_from_function_and_args(function, args, kwargs):
|
||||||
|
"""Find an arg of type RequestContext and return it.
|
||||||
|
|
||||||
|
This is useful in a couple of decorators where we don't
|
||||||
|
know much about the function we're wrapping.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for arg in itertools.chain(kwargs.values(), args):
|
||||||
|
if isinstance(arg, RequestContext):
|
||||||
|
return arg
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def is_user_context(context):
|
||||||
|
"""Indicates if the request context is a normal user."""
|
||||||
|
if not context:
|
||||||
|
return False
|
||||||
|
if context.is_admin:
|
||||||
|
return False
|
||||||
|
if not context.user_id or not context.project_id:
|
||||||
|
return False
|
||||||
|
return True
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Copyright (c) 2014 Rackspace, Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""WSGI callable for WSGI containers
|
||||||
|
|
||||||
|
This app should be used by external WSGI
|
||||||
|
containers. For example:
|
||||||
|
|
||||||
|
$ gunicorn dory.transport.app:app
|
||||||
|
|
||||||
|
NOTE: As for external containers, it is necessary
|
||||||
|
to put config files in the standard paths. There's
|
||||||
|
no common way to specify / pass configuration files
|
||||||
|
to the WSGI app when it is called from other apps.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from oslo.config import cfg
|
||||||
|
|
||||||
|
from dory import bootstrap
|
||||||
|
|
||||||
|
|
||||||
|
conf = cfg.CONF
|
||||||
|
conf(project='cdn', prog='cdn', args=[])
|
||||||
|
|
||||||
|
app = bootstrap.Bootstrap(conf).transport.app
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Copyright (c) 2014 Rackspace, Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Pecan Transport Driver"""
|
||||||
|
|
||||||
|
from cdn.transport.pecan import driver
|
||||||
|
|
||||||
|
|
||||||
|
# Hoist into package namespace
|
||||||
|
Driver = driver.PecanTransportDriver
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Copyright (c) 2014 Rackspace, Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Pecan Controllers"""
|
||||||
|
|
||||||
|
from cdn.transport.pecan.controllers import root
|
||||||
|
from cdn.transport.pecan.controllers import services
|
||||||
|
from cdn.transport.pecan.controllers import v1
|
||||||
|
|
||||||
|
|
||||||
|
# Hoist into package namespace
|
||||||
|
Root = root.RootController
|
||||||
|
Services = services.ServicesController
|
||||||
|
V1 = v1.V1Controller
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Copyright (c) 2014 Rackspace, Inc.
|
||||||
|
#
|
||||||
|
# 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 pecan import rest
|
||||||
|
|
||||||
|
|
||||||
|
class Controller(rest.RestController):
|
||||||
|
|
||||||
|
def __init__(self, driver):
|
||||||
|
self._driver = driver
|
||||||
|
|
||||||
|
def add_controller(self, path, controller):
|
||||||
|
setattr(self, path, controller)
|
|
@ -0,0 +1,51 @@
|
||||||
|
# Copyright (c) 2014 Rackspace, Inc.
|
||||||
|
#
|
||||||
|
# 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 re
|
||||||
|
|
||||||
|
import pecan
|
||||||
|
|
||||||
|
from cdn.transport.pecan.controllers import base
|
||||||
|
|
||||||
|
|
||||||
|
class RootController(base.Controller):
|
||||||
|
|
||||||
|
def __init__(self, driver):
|
||||||
|
super(RootController, self).__init__(driver)
|
||||||
|
self.paths = []
|
||||||
|
|
||||||
|
def add_controller(self, path, controller):
|
||||||
|
super(RootController, self).add_controller(path, controller)
|
||||||
|
self.paths.append(path)
|
||||||
|
|
||||||
|
@pecan.expose()
|
||||||
|
def _route(self, args, request=None):
|
||||||
|
# Optionally allow OpenStack project ID in the URL
|
||||||
|
# Remove it from the URL if it's present
|
||||||
|
# ['v1', 'todos'] or ['v1', '123', 'todos']
|
||||||
|
if (
|
||||||
|
len(args) >= 2
|
||||||
|
and args[0] in self.paths
|
||||||
|
and re.match('^[0-9]+$', args[1])
|
||||||
|
):
|
||||||
|
args.pop(1)
|
||||||
|
|
||||||
|
return super(RootController, self)._route(args, request)
|
||||||
|
|
||||||
|
@pecan.expose('json')
|
||||||
|
def get_all(self):
|
||||||
|
return {
|
||||||
|
'status': 'up',
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
# Copyright (c) 2014 Rackspace, Inc.
|
||||||
|
#
|
||||||
|
# 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 uuid
|
||||||
|
|
||||||
|
import pecan
|
||||||
|
|
||||||
|
from cdn.transport.pecan.controllers import base
|
||||||
|
|
||||||
|
|
||||||
|
class ServicesController(base.Controller):
|
||||||
|
|
||||||
|
@pecan.expose('json')
|
||||||
|
def get_all(self):
|
||||||
|
tenant_id = pecan.request.context.to_dict()['tenant']
|
||||||
|
services_controller = self._driver.manager.services_controller
|
||||||
|
return services_controller.list(tenant_id)
|
||||||
|
|
||||||
|
@pecan.expose('json')
|
||||||
|
def get_one(self):
|
||||||
|
tenant_id = pecan.request.context.to_dict()['tenant']
|
||||||
|
services_controller = self._driver.manager.services_controller
|
||||||
|
return services_controller.list(tenant_id)
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Copyright (c) 2014 Rackspace, Inc.
|
||||||
|
#
|
||||||
|
# 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 pecan
|
||||||
|
|
||||||
|
from cdn.transport.pecan.controllers import base
|
||||||
|
|
||||||
|
|
||||||
|
class V1Controller(base.Controller):
|
||||||
|
|
||||||
|
@pecan.expose('json')
|
||||||
|
def get(self):
|
||||||
|
v1_controller = self._driver.manager.v1_controller
|
||||||
|
return v1_controller.get()
|
|
@ -0,0 +1,74 @@
|
||||||
|
# Copyright (c) 2014 Rackspace, Inc.
|
||||||
|
#
|
||||||
|
# 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 wsgiref import simple_server
|
||||||
|
|
||||||
|
import pecan
|
||||||
|
from oslo.config import cfg
|
||||||
|
|
||||||
|
from cdn.openstack.common import log
|
||||||
|
from cdn import transport
|
||||||
|
from cdn.transport.pecan import controllers
|
||||||
|
from cdn.transport.pecan import hooks
|
||||||
|
|
||||||
|
|
||||||
|
_PECAN_OPTIONS = [
|
||||||
|
cfg.StrOpt('bind', default='127.0.0.1',
|
||||||
|
help='Address on which the self-hosting server will listen'),
|
||||||
|
cfg.IntOpt('port', default=8888,
|
||||||
|
help='Port on which the self-hosting server will listen'),
|
||||||
|
]
|
||||||
|
|
||||||
|
_PECAN_GROUP = 'drivers:transport:pecan'
|
||||||
|
|
||||||
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class PecanTransportDriver(transport.Driver):
|
||||||
|
|
||||||
|
def __init__(self, conf, manager):
|
||||||
|
super(PecanTransportDriver, self).__init__(conf, manager)
|
||||||
|
|
||||||
|
self._conf.register_opts(_PECAN_OPTIONS, group=_PECAN_GROUP)
|
||||||
|
self._pecan_conf = self._conf[_PECAN_GROUP]
|
||||||
|
|
||||||
|
self._setup_app()
|
||||||
|
|
||||||
|
def _setup_app(self):
|
||||||
|
root_controller = controllers.Root(self)
|
||||||
|
|
||||||
|
pecan_hooks = [hooks.Context()]
|
||||||
|
|
||||||
|
self._app = pecan.make_app(root_controller, hooks=pecan_hooks)
|
||||||
|
|
||||||
|
v1_controller = controllers.V1(self)
|
||||||
|
root_controller.add_controller('v1.0', v1_controller)
|
||||||
|
|
||||||
|
services_controller = controllers.Services(self)
|
||||||
|
v1_controller.add_controller('services', services_controller)
|
||||||
|
|
||||||
|
def listen(self):
|
||||||
|
LOG.info(
|
||||||
|
'Serving on host %(bind)s:%(port)s',
|
||||||
|
{
|
||||||
|
'bind': self._pecan_conf.bind,
|
||||||
|
'port': self._pecan_conf.port,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
httpd = simple_server.make_server(self._pecan_conf.bind,
|
||||||
|
self._pecan_conf.port,
|
||||||
|
self.app)
|
||||||
|
httpd.serve_forever()
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Copyright (c) 2014 Rackspace, Inc.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Pecan Hooks"""
|
||||||
|
|
||||||
|
from cdn.transport.pecan.hooks import context
|
||||||
|
|
||||||
|
|
||||||
|
# Hoist into package namespace
|
||||||
|
Context = context.ContextHook
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Copyright (c) 2014 Rackspace, Inc.
|
||||||
|
#
|
||||||
|
# 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 pecan import hooks
|
||||||
|
|
||||||
|
from cdn.openstack.common import context
|
||||||
|
from cdn.openstack.common import local
|
||||||
|
|
||||||
|
|
||||||
|
class ContextHook(hooks.PecanHook):
|
||||||
|
|
||||||
|
def on_route(self, state):
|
||||||
|
context_kwargs = {}
|
||||||
|
|
||||||
|
if 'X-Project-ID' in state.request.headers:
|
||||||
|
context_kwargs['tenant'] = state.request.headers['X-Project-ID']
|
||||||
|
|
||||||
|
if 'tenant' not in context_kwargs:
|
||||||
|
# Didn't find the X-Project-Id header, pull from URL instead
|
||||||
|
# Expects form /v1/{project_id}/path
|
||||||
|
context_kwargs['tenant'] = state.request.path.split('/')[2]
|
||||||
|
|
||||||
|
if 'X-Auth-Token' in state.request.headers:
|
||||||
|
context_kwargs['auth_token'] = state.request.headers['X-Auth-Token']
|
||||||
|
|
||||||
|
request_context = context.RequestContext(**context_kwargs)
|
||||||
|
state.request.context = request_context
|
||||||
|
local.store.context = request_context
|
|
@ -0,0 +1,41 @@
|
||||||
|
# Copyright (c) 2014 Rackspace, Inc.
|
||||||
|
#
|
||||||
|
# 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 os
|
||||||
|
|
||||||
|
from oslo.config import cfg
|
||||||
|
import testtools
|
||||||
|
import webtest
|
||||||
|
|
||||||
|
from cdn import bootstrap
|
||||||
|
|
||||||
|
|
||||||
|
class BaseFunctionalTest(testtools.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(BaseFunctionalTest, self).setUp()
|
||||||
|
|
||||||
|
tests_path = os.path.abspath(os.path.dirname(
|
||||||
|
os.path.dirname(
|
||||||
|
os.path.dirname(os.path.dirname(__file__)
|
||||||
|
))))
|
||||||
|
conf_path = os.path.join(tests_path, 'etc', 'default_functional.conf')
|
||||||
|
cfg.CONF(args=[], default_config_files=[conf_path])
|
||||||
|
cdn_wsgi = bootstrap.Bootstrap(cfg.CONF).transport.app
|
||||||
|
|
||||||
|
self.app = webtest.TestApp(cdn_wsgi)
|
||||||
|
|
||||||
|
|
||||||
|
FunctionalTest = BaseFunctionalTest
|
|
@ -0,0 +1,10 @@
|
||||||
|
from tests.functional.transport.pecan import base
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceControllerTest(base.FunctionalTest):
|
||||||
|
|
||||||
|
def test_get_all(self):
|
||||||
|
pass
|
||||||
|
#response = self.app.get('/health')
|
||||||
|
|
||||||
|
#self.assertEqual(204, response.status_code)
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Copyright (c) 2014 Rackspace, Inc.
|
||||||
|
#
|
||||||
|
# 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 tests.functional.transport.pecan import base
|
||||||
|
|
||||||
|
|
||||||
|
class ServicesControllerTest(base.FunctionalTest):
|
||||||
|
|
||||||
|
def test_get_all(self):
|
||||||
|
response = self.app.get('/v1.0/00001/services')
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Copyright (c) 2014 Rackspace, Inc.
|
||||||
|
#
|
||||||
|
# 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 cdn.manager.default import v1
|
||||||
|
|
||||||
|
from tests.functional.transport.pecan import base
|
||||||
|
|
||||||
|
|
||||||
|
class V1ControllerTest(base.FunctionalTest):
|
||||||
|
|
||||||
|
def test_get_all(self):
|
||||||
|
response = self.app.get('/v1.0/00001')
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
# Temporary until actual implementation
|
||||||
|
self.assertEqual(v1.JSON_HOME, response.json)
|
|
@ -0,0 +1,43 @@
|
||||||
|
# Copyright (c) 2014 Rackspace, Inc.
|
||||||
|
#
|
||||||
|
# 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 uuid
|
||||||
|
|
||||||
|
from cdn.manager.default import v1
|
||||||
|
from tests.functional.transport.pecan import base
|
||||||
|
|
||||||
|
|
||||||
|
class ContextHookTest(base.FunctionalTest):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(ContextHookTest, self).setUp()
|
||||||
|
|
||||||
|
self.headers = {'X-Auth-Token': str(uuid.uuid4())}
|
||||||
|
|
||||||
|
def test_project_id_in_header(self):
|
||||||
|
self.headers['X-Project-Id'] = '000001'
|
||||||
|
response = self.app.get('/v1.0', headers=self.headers)
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
|
||||||
|
# Temporary until actual implementation
|
||||||
|
self.assertEqual(v1.JSON_HOME, response.json)
|
||||||
|
|
||||||
|
def test_project_id_in_url(self):
|
||||||
|
response = self.app.get('/v1.0/000001', headers=self.headers)
|
||||||
|
|
||||||
|
self.assertEqual(200, response.status_code)
|
||||||
|
# Temporary until actual implementation
|
||||||
|
self.assertEqual(v1.JSON_HOME, response.json)
|
|
@ -0,0 +1,77 @@
|
||||||
|
# Copyright (c) 2014 Rackspace, Inc.
|
||||||
|
#
|
||||||
|
# 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 unittest
|
||||||
|
import threading
|
||||||
|
import ctypes
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from oslo.config import cfg
|
||||||
|
|
||||||
|
from cdn.transport.pecan import driver
|
||||||
|
|
||||||
|
|
||||||
|
def terminate_thread(thread):
|
||||||
|
"""Terminates a python thread from another thread.
|
||||||
|
|
||||||
|
:param thread: a threading.Thread instance
|
||||||
|
"""
|
||||||
|
if not thread.isAlive():
|
||||||
|
return
|
||||||
|
|
||||||
|
exc = ctypes.py_object(SystemExit)
|
||||||
|
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
|
||||||
|
ctypes.c_long(thread.ident), exc)
|
||||||
|
if res == 0:
|
||||||
|
raise ValueError("nonexistent thread id")
|
||||||
|
elif res > 1:
|
||||||
|
# """if it returns a number greater than one, you're in trouble,
|
||||||
|
# and you should call it again with exc=NULL to revert the effect"""
|
||||||
|
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread.ident, None)
|
||||||
|
raise SystemError("PyThreadState_SetAsyncExc failed")
|
||||||
|
|
||||||
|
|
||||||
|
class StoppableThread(threading.Thread):
|
||||||
|
"""Thread class with a stop() method. The thread itself has to check
|
||||||
|
regularly for the stopped() condition."""
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
super(StoppableThread, self).__init__(**kwargs)
|
||||||
|
self._stop = threading.Event()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self._stop.set()
|
||||||
|
|
||||||
|
def stopped(self):
|
||||||
|
return self._stop.isSet()
|
||||||
|
|
||||||
|
|
||||||
|
class TestPecanDriver(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
# Let manager = None for now
|
||||||
|
self.pecan_driver = driver.PecanTransportDriver(cfg.CONF, None)
|
||||||
|
|
||||||
|
|
||||||
|
def test_app_created(self):
|
||||||
|
self.assertEquals(self.pecan_driver.app is not None, True)
|
||||||
|
t = StoppableThread(target = self.pecan_driver.listen)
|
||||||
|
t.start()
|
||||||
|
#r = requests.get('http://127.0.0.1:8888')
|
||||||
|
#print r
|
||||||
|
#assertR
|
||||||
|
t.stop()
|
||||||
|
terminate_thread(t)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue