Use aioservice v0.0.1

This commit is contained in:
Denis Makogon 2016-11-22 17:16:43 +02:00
parent fd43c0f157
commit 15710f6cd4
15 changed files with 102 additions and 251 deletions

View File

@ -70,16 +70,16 @@ echo -e "Show app public route\n"
curl ${LAOS_API_URL}/v1/${OS_PROJECT_ID}/apps/${app_name}/routes/hello-sync-public -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool curl ${LAOS_API_URL}/v1/${OS_PROJECT_ID}/apps/${app_name}/routes/hello-sync-public -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool
echo -e "Running app sync private route\n" echo -e "Running app sync private route\n"
curl -X POST -d '{"name": "Johnny"}' ${LAOS_API_URL}/private/${OS_PROJECT_ID}/${app_name}/hello-sync-private -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool curl -X POST -d '{"name": "Johnny"}' ${LAOS_API_URL}/v1/r/${OS_PROJECT_ID}/${app_name}/hello-sync-private -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool
echo -e "Running app sync public route\n" echo -e "Running app sync public route\n"
curl -X POST -d '{"name": "Johnny"}' ${LAOS_API_URL}/public/${app_name}/hello-sync-public -H "Content-Type: application/json" | python3 -mjson.tool curl -X POST -d '{"name": "Johnny"}' ${LAOS_API_URL}/r/${app_name}/hello-sync-public -H "Content-Type: application/json" | python3 -mjson.tool
echo -e "Creating app async route\n" echo -e "Creating app async route\n"
curl -X POST -d '{"route":{"type": "async", "path": "/hello-async-private", "image": "iron/hello", "is_public": "false"}}' ${LAOS_API_URL}/v1/${OS_PROJECT_ID}/apps/${app_name}/routes -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool curl -X POST -d '{"route":{"type": "async", "path": "/hello-async-private", "image": "iron/hello", "is_public": "false"}}' ${LAOS_API_URL}/v1/${OS_PROJECT_ID}/apps/${app_name}/routes -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool
echo -e "Running app async route\n" echo -e "Running app async route\n"
curl -X POST -d '{"name": "Johnny"}' ${LAOS_API_URL}/private/${OS_PROJECT_ID}/${app_name}/hello-async-private -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool curl -X POST -d '{"name": "Johnny"}' ${LAOS_API_URL}/v1/r/${OS_PROJECT_ID}/${app_name}/hello-async-private -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool
echo -e "Deleting app route\n" echo -e "Deleting app route\n"
curl -X DELETE ${LAOS_API_URL}/v1/${OS_PROJECT_ID}/apps/${app_name}/routes/hello-sync-public -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool curl -X DELETE ${LAOS_API_URL}/v1/${OS_PROJECT_ID}/apps/${app_name}/routes/hello-sync-public -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool

View File

@ -14,20 +14,20 @@
from aiohttp import web from aiohttp import web
from aioservice.http import controller
from aioservice.http import requests
from laos.api.views import app as app_view from laos.api.views import app as app_view
from laos.common.base import controllers
from laos.common import config from laos.common import config
from laos.models import app as app_model from laos.models import app as app_model
class AppV1Controller(controllers.ServiceControllerBase): class AppV1Controller(controller.ServiceController):
controller_name = "apps" controller_name = "apps"
version = "v1" version = "v1"
@controllers.api_action(method='GET', route='{project_id}/apps') @requests.api_action(method='GET', route='{project_id}/apps')
async def list(self, request, **kwargs): async def list(self, request, **kwargs):
""" """
--- ---
@ -59,7 +59,7 @@ class AppV1Controller(controllers.ServiceControllerBase):
status=200 status=200
) )
@controllers.api_action(method='POST', route='{project_id}/apps') @requests.api_action(method='POST', route='{project_id}/apps')
async def create(self, request, **kwargs): async def create(self, request, **kwargs):
""" """
--- ---
@ -118,7 +118,7 @@ class AppV1Controller(controllers.ServiceControllerBase):
}, status=200 }, status=200
) )
@controllers.api_action(method='GET', route='{project_id}/apps/{app}') @requests.api_action(method='GET', route='{project_id}/apps/{app}')
async def get(self, request, **kwargs): async def get(self, request, **kwargs):
""" """
--- ---
@ -161,7 +161,7 @@ class AppV1Controller(controllers.ServiceControllerBase):
) )
# TODO(denismakogon): disabled until iron-io/functions/pull/259 # TODO(denismakogon): disabled until iron-io/functions/pull/259
# #
# @controllers.api_action(method='PUT', route='{project_id}/apps/{app}') # @requests.api_action(method='PUT', route='{project_id}/apps/{app}')
# async def update(self, request, **kwargs): # async def update(self, request, **kwargs):
# log = config.Config.config_instance().logger # log = config.Config.config_instance().logger
# project_id = request.match_info.get('project_id') # project_id = request.match_info.get('project_id')
@ -176,7 +176,7 @@ class AppV1Controller(controllers.ServiceControllerBase):
# status=200 # status=200
# ) # )
@controllers.api_action(method='DELETE', route='{project_id}/apps/{app}') @requests.api_action(method='DELETE', route='{project_id}/apps/{app}')
async def delete(self, request, **kwargs): async def delete(self, request, **kwargs):
""" """
--- ---

View File

@ -16,20 +16,20 @@ import json
from aiohttp import web from aiohttp import web
from aioservice.http import controller
from aioservice.http import requests
from laos.api.views import app as app_view from laos.api.views import app as app_view
from laos.common.base import controllers
from laos.common import config from laos.common import config
from laos.models import app as app_model from laos.models import app as app_model
class AppRouteV1Controller(controllers.ServiceControllerBase): class AppRouteV1Controller(controller.ServiceController):
controller_name = "routes" controller_name = "routes"
version = "v1" version = "v1"
@controllers.api_action( @requests.api_action(
method='GET', route='{project_id}/apps/{app}/routes') method='GET', route='{project_id}/apps/{app}/routes')
async def list(self, request, **kwargs): async def list(self, request, **kwargs):
""" """
@ -82,7 +82,7 @@ class AppRouteV1Controller(controllers.ServiceControllerBase):
"message": "Successfully loaded app routes", "message": "Successfully loaded app routes",
}, status=200) }, status=200)
@controllers.api_action( @requests.api_action(
method='POST', route='{project_id}/apps/{app}/routes') method='POST', route='{project_id}/apps/{app}/routes')
async def create(self, request, **kwargs): async def create(self, request, **kwargs):
""" """
@ -185,7 +185,7 @@ class AppRouteV1Controller(controllers.ServiceControllerBase):
"message": "App route successfully created" "message": "App route successfully created"
}, status=200) }, status=200)
@controllers.api_action( @requests.api_action(
method='GET', route='{project_id}/apps/{app}/routes/{route}') method='GET', route='{project_id}/apps/{app}/routes/{route}')
async def get(self, request, **kwargs): async def get(self, request, **kwargs):
""" """
@ -248,7 +248,7 @@ class AppRouteV1Controller(controllers.ServiceControllerBase):
"message": "App route successfully loaded" "message": "App route successfully loaded"
}, status=200) }, status=200)
@controllers.api_action( @requests.api_action(
method='DELETE', route='{project_id}/apps/{app}/routes/{route}') method='DELETE', route='{project_id}/apps/{app}/routes/{route}')
async def delete(self, request, **kwargs): async def delete(self, request, **kwargs):
""" """

View File

@ -14,7 +14,9 @@
from aiohttp import web from aiohttp import web
from laos.common.base import controllers from aioservice.http import controller
from aioservice.http import requests
from laos.common import config from laos.common import config
@ -64,14 +66,14 @@ class RunnableMixin(object):
return web.json_response(status=200, data=process_result(result)) return web.json_response(status=200, data=process_result(result))
class PublicRunnableV1Controller(controllers.ServiceControllerBase, class PublicRunnableV1Controller(controller.ServiceController,
RunnableMixin): RunnableMixin):
controller_name = "public_runnable" controller_name = "public_runnable"
# IronFunction uses `r` as runnable instead API version # IronFunction uses `r` as runnable instead API version
version = "r" version = "r"
@controllers.api_action( @requests.api_action(
method='POST', route='{app}/{route}') method='POST', route='{app}/{route}')
async def run(self, request, **kwargs): async def run(self, request, **kwargs):
""" """
@ -93,15 +95,15 @@ class PublicRunnableV1Controller(controllers.ServiceControllerBase,
self).run(request, **kwargs) self).run(request, **kwargs)
class RunnableV1Controller(controllers.ServiceControllerBase, class RunnableV1Controller(controller.ServiceController,
RunnableMixin): RunnableMixin):
controller_name = "runnable" controller_name = "runnable"
# IronFunction uses `r` as runnable instead API version # IronFunction uses `r` as runnable instead API version
version = "r" version = "v1"
@controllers.api_action( @requests.api_action(
method='POST', route='{project_id}/{app}/{route}') method='POST', route='r/{project_id}/{app}/{route}')
async def run(self, request, **kwargs): async def run(self, request, **kwargs):
""" """
--- ---

View File

@ -16,13 +16,15 @@ from aiohttp import web
# from laos.models import app as app_model # from laos.models import app as app_model
from laos.common.base import controllers from aioservice.http import controller
from aioservice.http import requests
# from laos.common import config # from laos.common import config
# TODO(denismakogon): disabled until # TODO(denismakogon): disabled until
# https://github.com/iron-io/functions/issues/275 # https://github.com/iron-io/functions/issues/275
class TasksV1Controller(controllers.ServiceControllerBase): class TasksV1Controller(controller.ServiceController):
controller_name = "tasks" controller_name = "tasks"
version = "v1" version = "v1"
@ -33,7 +35,7 @@ class TasksV1Controller(controllers.ServiceControllerBase):
# - on each request check if route is public our private # - on each request check if route is public our private
# * reject with 401 if route is private # * reject with 401 if route is private
# * accept with 200 if route is public # * accept with 200 if route is public
@controllers.api_action( @requests.api_action(
method='GET', route='{project_id}/tasks') method='GET', route='{project_id}/tasks')
async def list(self, request, **kwargs): async def list(self, request, **kwargs):
""" """
@ -63,7 +65,7 @@ class TasksV1Controller(controllers.ServiceControllerBase):
} }
}, status=405) }, status=405)
@controllers.api_action( @requests.api_action(
method='GET', route='{project_id}/tasks/{task}') method='GET', route='{project_id}/tasks/{task}')
async def show(self, request, **kwargs): async def show(self, request, **kwargs):
""" """

View File

@ -45,11 +45,11 @@ class AppRouteView(object):
view = [] view = []
for route in self.routes: for route in self.routes:
if not route.is_public: if not route.is_public:
path = ("{}/private/{}/{}{}".format( path = ("{}/v1/r/{}/{}{}".format(
self.api_url, self.project_id, self.api_url, self.project_id,
route.appname, route.path)) route.appname, route.path))
else: else:
path = ("{}/public/{}{}".format( path = ("{}/r/{}{}".format(
self.api_url, route.appname, route.path)) self.api_url, route.appname, route.path))
view.append({ view.append({
"path": path, "path": path,

View File

@ -1,77 +0,0 @@
# 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.
import functools
import inspect
from aiohttp import web
class ServiceControllerBase(object):
controller_name = 'abstract'
version = ""
def __get_handlers(self):
# when this method gets executed by child classes
# method list includes a method of parent class,
# so this code ignores it because it doesn't belong to controllers
methods = [getattr(self, _m)
for _m in dir(self) if inspect.ismethod(
getattr(self, _m)) and "__" not in _m]
return [[method,
method.arg_method,
method.arg_route] for method in methods]
def __init__(self, sub_service: web.Application):
for fn, http_method, route in self.__get_handlers():
proxy_fn = '_'.join([fn.__name__, self.controller_name])
setattr(self, proxy_fn, fn)
sub_service.router.add_route(
http_method, "/{}".format(route),
getattr(self, proxy_fn), name=proxy_fn)
def api_action(**outter_kwargs):
"""
Wrapps API controller action actions handler
:param outter_kwargs: API instance action key-value args
:return: _api_handler
Example:
class Controller(ControllerBase):
@api_action(method='GET')
async def index(self, request, **kwargs):
return web.Response(
text=str(request),
reason="dumb API",
status=201)
"""
def _api_handler(func):
@functools.wraps(func)
async def wrapper(self, *args, **kwargs):
return await func(self, *args, *kwargs)
for key, value in outter_kwargs.items():
setattr(wrapper, 'arg_{}'.format(key), value)
setattr(wrapper, 'is_module_function', True)
return wrapper
return _api_handler

View File

@ -1,86 +0,0 @@
# 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.
import asyncio
import aiohttp_swagger
from aiohttp import web
from laos.common import logger as log
class AbstractWebServer(object):
def __init__(self, host: str='127.0.0.1',
port: int= '10001',
private_controllers: dict=None,
private_middlewares: list=None,
public_middlewares: list=None,
public_controllers: dict=None,
event_loop: asyncio.AbstractEventLoop=None,
logger=log.UnifiedLogger(
log_to_console=True,
level="INFO").setup_logger(__name__),
debug=False):
"""
HTTP server abstraction class
:param host: Bind host
:param port: Bind port
:param private_controllers: private API controllers mapping
:param private_middlewares: list of private API middleware
:param public_middlewares:
list of public API middleware
:param public_controllers:
public API controllers mapping
:param event_loop: asyncio eventloop
:param logger: logging.Logger
"""
self.host = host
self.port = port
self.event_loop = event_loop
self.logger = logger
self.root_service = web.Application(
logger=self.logger,
loop=self.event_loop,
debug=debug
)
self.register_subapps(private_controllers, private_middlewares)
self.register_subapps(public_controllers, public_middlewares)
def _apply_routers(self, service, controllers):
for controller in controllers:
controller(service)
return service
def register_subapps(self, controllers_mapping: dict, middlewares: list):
if controllers_mapping:
for sub_route, controllers in controllers_mapping.items():
service = self._apply_routers(
web.Application(
logger=self.logger,
loop=self.event_loop,
middlewares=middlewares
if middlewares else []),
controllers)
self.root_service.router.add_subapp(
"/{}/".format(sub_route), service)
def initialize(self):
aiohttp_swagger.setup_swagger(
self.root_service, swagger_url="/api")
web.run_app(self.root_service, host=self.host, port=self.port,
shutdown_timeout=10, access_log=self.logger)

View File

@ -21,7 +21,7 @@ from laos.common import utils
def common_logger_setup( def common_logger_setup(
level=logging.DEBUG, level=logging.DEBUG,
filename='/tmp/aiorchestra.log', filename='/tmp/laos-api.log',
log_formatter='[%(asctime)s] - ' log_formatter='[%(asctime)s] - '
'%(name)s - ' '%(name)s - '
'%(levelname)s - ' '%(levelname)s - '

View File

@ -17,6 +17,8 @@ import asyncio
import click import click
import uvloop import uvloop
from aioservice.http import service
from laos.api.controllers import apps from laos.api.controllers import apps
from laos.api.controllers import routes from laos.api.controllers import routes
from laos.api.controllers import runnable from laos.api.controllers import runnable
@ -25,46 +27,46 @@ from laos.api.controllers import tasks
from laos.api.middleware import content_type from laos.api.middleware import content_type
from laos.api.middleware import keystone from laos.api.middleware import keystone
from laos.common.base import service
from laos.common import config from laos.common import config
from laos.common import logger as log from laos.common import logger as log
class API(service.AbstractWebServer): class API(service.HTTPService):
def __init__(self, host: str='0.0.0.0', def __init__(self, host: str='0.0.0.0',
port: int=10001, port: int=10001,
loop: asyncio.AbstractEventLoop=asyncio.get_event_loop(), loop: asyncio.AbstractEventLoop=asyncio.get_event_loop(),
logger=None, logger=None,
debug=False): debug=False):
v1_service = service.VersionedService(
[
apps.AppV1Controller,
routes.AppRouteV1Controller,
runnable.RunnableV1Controller,
tasks.TasksV1Controller
], middleware=[
keystone.auth_through_token,
content_type.content_type_validator
])
public_runnable_service = service.VersionedService(
[
runnable.PublicRunnableV1Controller
], middleware=[
content_type.content_type_validator,
]
)
super(API, self).__init__( super(API, self).__init__(
host=host, host=host,
port=port, port=port,
private_controllers={
"v1": [
apps.AppV1Controller,
routes.AppRouteV1Controller,
tasks.TasksV1Controller,
],
"private": [
runnable.RunnableV1Controller,
]
},
public_controllers={
"public": [
runnable.PublicRunnableV1Controller,
],
},
private_middlewares=[
keystone.auth_through_token,
content_type.content_type_validator,
],
public_middlewares=[
content_type.content_type_validator,
],
event_loop=loop, event_loop=loop,
logger=logger, logger=logger,
debug=debug, debug=debug,
subservice_definitions=[
v1_service, public_runnable_service
]
) )
@ -126,8 +128,15 @@ def server(host, port, db_uri,
event_loop=loop, event_loop=loop,
) )
API(host=host, port=port, loop=loop, API(
logger=logger, debug=debug).initialize() host=host, port=port, loop=loop,
logger=logger, debug=debug
).apply_swagger(
swagger_url="/api",
description="Laos API service docs",
api_version="v1.0.0",
title="Laos API",
).initialize()
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -17,13 +17,14 @@ import os
import testtools import testtools
import uuid import uuid
from aioservice.http import service
from laos.api.controllers import apps from laos.api.controllers import apps
from laos.api.controllers import routes from laos.api.controllers import routes
from laos.api.controllers import runnable from laos.api.controllers import runnable
from laos.api.controllers import tasks from laos.api.controllers import tasks
from laos.api.middleware import content_type from laos.api.middleware import content_type
from laos.common.base import service
from laos.common import config from laos.common import config
from laos.tests.common import base from laos.tests.common import base
@ -36,34 +37,30 @@ class LaosFunctionalTestsBase(base.LaosTestsBase, testtools.TestCase):
def setUp(self): def setUp(self):
self.testloop, logger = self.get_loop_and_logger("functional") self.testloop, logger = self.get_loop_and_logger("functional")
self.testapp = service.AbstractWebServer( v1_service = service.VersionedService(
host="localhost", [
apps.AppV1Controller,
routes.AppRouteV1Controller,
tasks.TasksV1Controller,
runnable.RunnableV1Controller,
], middleware=[
content_type.content_type_validator
]
)
public_runnable = service.VersionedService(
[
runnable.PublicRunnableV1Controller,
], middleware=[
content_type.content_type_validator,
]
)
self.testapp = service.HTTPService(
[v1_service, public_runnable],
port=10001, port=10001,
private_controllers={
"v1": [
apps.AppV1Controller,
routes.AppRouteV1Controller,
tasks.TasksV1Controller,
],
"private": [
runnable.RunnableV1Controller,
]
},
public_controllers={
"public": [
runnable.PublicRunnableV1Controller,
],
},
private_middlewares=[
content_type.content_type_validator,
],
public_middlewares=[
content_type.content_type_validator,
],
event_loop=self.testloop, event_loop=self.testloop,
logger=logger, logger=logger,
debug=True, debug=True,
).root_service ).root
connection_pool = config.Connection( connection_pool = config.Connection(
os.getenv("TEST_DB_URI"), loop=self.testloop) os.getenv("TEST_DB_URI"), loop=self.testloop)

View File

@ -60,7 +60,7 @@ class LaosIntegrationTestsBase(base.LaosTestsBase, testtools.TestCase):
loop=self.testloop, logger=logger, debug=True) loop=self.testloop, logger=logger, debug=True)
self.test_client = client.ProjectBoundLaosTestClient( self.test_client = client.ProjectBoundLaosTestClient(
self.test_app.root_service, project_id, headers={ self.test_app.root, project_id, headers={
"X-Auth-Token": os_token "X-Auth-Token": os_token
}) })
self.testloop.run_until_complete( self.testloop.run_until_complete(

View File

@ -3,11 +3,15 @@
# process, which may cause wedges in the gate later. # process, which may cause wedges in the gate later.
uvloop==0.6.0 # Apache-2.0 uvloop==0.6.0 # Apache-2.0
aiohttp==1.1.5 # Apache-2.0 aioservice==0.0.1 # Apache-2.0
aiomysql==0.0.9 # Apache-2.0 aiomysql==0.0.9 # Apache-2.0
alembic==0.8.8 # MIT alembic==0.8.8 # MIT
click==6.6 # Apache-2.0 click==6.6 # Apache-2.0
# IronFunctions
python-functionsclient==0.0.1 python-functionsclient==0.0.1
# OpenStack
keystoneauth1==2.15.0 # Apache-2.0 keystoneauth1==2.15.0 # Apache-2.0
python-keystoneclient==3.6.0 # Apache-2.0 python-keystoneclient==3.6.0 # Apache-2.0

View File

@ -30,7 +30,7 @@ setuptools.setup(
packages=setuptools.find_packages(), packages=setuptools.find_packages(),
install_requires=[ install_requires=[
"uvloop==0.6.0", "uvloop==0.6.0",
"aiohttp==1.1.5", "aioservice==0.0.1",
"aiomysql==0.0.9", "aiomysql==0.0.9",
"alembic==0.8.8", "alembic==0.8.8",
"click==6.6", "click==6.6",