From 15710f6cd45c8626d03f6aa02b9c36521a0b76d8 Mon Sep 17 00:00:00 2001 From: Denis Makogon Date: Tue, 22 Nov 2016 17:16:43 +0200 Subject: [PATCH] Use aioservice v0.0.1 --- examples/hello-lambda.sh | 6 +-- laos/api/controllers/apps.py | 18 +++---- laos/api/controllers/routes.py | 16 +++--- laos/api/controllers/runnable.py | 16 +++--- laos/api/controllers/tasks.py | 10 ++-- laos/api/views/app.py | 4 +- laos/common/base/__init__.py | 0 laos/common/base/controllers.py | 77 ---------------------------- laos/common/base/service.py | 86 -------------------------------- laos/common/logger.py | 2 +- laos/service/laos_api.py | 61 ++++++++++++---------- laos/tests/functional/base.py | 47 ++++++++--------- laos/tests/integration/base.py | 2 +- requirements.txt | 6 ++- setup.py | 2 +- 15 files changed, 102 insertions(+), 251 deletions(-) delete mode 100644 laos/common/base/__init__.py delete mode 100644 laos/common/base/controllers.py delete mode 100644 laos/common/base/service.py diff --git a/examples/hello-lambda.sh b/examples/hello-lambda.sh index f21831e..88ee696 100755 --- a/examples/hello-lambda.sh +++ b/examples/hello-lambda.sh @@ -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 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" -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" 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" -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" 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 diff --git a/laos/api/controllers/apps.py b/laos/api/controllers/apps.py index 90447a1..bde58cc 100644 --- a/laos/api/controllers/apps.py +++ b/laos/api/controllers/apps.py @@ -14,20 +14,20 @@ 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.common.base import controllers from laos.common import config - from laos.models import app as app_model -class AppV1Controller(controllers.ServiceControllerBase): +class AppV1Controller(controller.ServiceController): controller_name = "apps" 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): """ --- @@ -59,7 +59,7 @@ class AppV1Controller(controllers.ServiceControllerBase): 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): """ --- @@ -118,7 +118,7 @@ class AppV1Controller(controllers.ServiceControllerBase): }, 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): """ --- @@ -161,7 +161,7 @@ class AppV1Controller(controllers.ServiceControllerBase): ) # 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): # log = config.Config.config_instance().logger # project_id = request.match_info.get('project_id') @@ -176,7 +176,7 @@ class AppV1Controller(controllers.ServiceControllerBase): # 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): """ --- diff --git a/laos/api/controllers/routes.py b/laos/api/controllers/routes.py index e3ffa73..886f55a 100644 --- a/laos/api/controllers/routes.py +++ b/laos/api/controllers/routes.py @@ -16,20 +16,20 @@ import json 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.common.base import controllers from laos.common import config - from laos.models import app as app_model -class AppRouteV1Controller(controllers.ServiceControllerBase): +class AppRouteV1Controller(controller.ServiceController): controller_name = "routes" version = "v1" - @controllers.api_action( + @requests.api_action( method='GET', route='{project_id}/apps/{app}/routes') async def list(self, request, **kwargs): """ @@ -82,7 +82,7 @@ class AppRouteV1Controller(controllers.ServiceControllerBase): "message": "Successfully loaded app routes", }, status=200) - @controllers.api_action( + @requests.api_action( method='POST', route='{project_id}/apps/{app}/routes') async def create(self, request, **kwargs): """ @@ -185,7 +185,7 @@ class AppRouteV1Controller(controllers.ServiceControllerBase): "message": "App route successfully created" }, status=200) - @controllers.api_action( + @requests.api_action( method='GET', route='{project_id}/apps/{app}/routes/{route}') async def get(self, request, **kwargs): """ @@ -248,7 +248,7 @@ class AppRouteV1Controller(controllers.ServiceControllerBase): "message": "App route successfully loaded" }, status=200) - @controllers.api_action( + @requests.api_action( method='DELETE', route='{project_id}/apps/{app}/routes/{route}') async def delete(self, request, **kwargs): """ diff --git a/laos/api/controllers/runnable.py b/laos/api/controllers/runnable.py index a442441..7a83a20 100644 --- a/laos/api/controllers/runnable.py +++ b/laos/api/controllers/runnable.py @@ -14,7 +14,9 @@ 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 @@ -64,14 +66,14 @@ class RunnableMixin(object): return web.json_response(status=200, data=process_result(result)) -class PublicRunnableV1Controller(controllers.ServiceControllerBase, +class PublicRunnableV1Controller(controller.ServiceController, RunnableMixin): controller_name = "public_runnable" # IronFunction uses `r` as runnable instead API version version = "r" - @controllers.api_action( + @requests.api_action( method='POST', route='{app}/{route}') async def run(self, request, **kwargs): """ @@ -93,15 +95,15 @@ class PublicRunnableV1Controller(controllers.ServiceControllerBase, self).run(request, **kwargs) -class RunnableV1Controller(controllers.ServiceControllerBase, +class RunnableV1Controller(controller.ServiceController, RunnableMixin): controller_name = "runnable" # IronFunction uses `r` as runnable instead API version - version = "r" + version = "v1" - @controllers.api_action( - method='POST', route='{project_id}/{app}/{route}') + @requests.api_action( + method='POST', route='r/{project_id}/{app}/{route}') async def run(self, request, **kwargs): """ --- diff --git a/laos/api/controllers/tasks.py b/laos/api/controllers/tasks.py index 27dce9b..dc6adda 100644 --- a/laos/api/controllers/tasks.py +++ b/laos/api/controllers/tasks.py @@ -16,13 +16,15 @@ from aiohttp import web # 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 # TODO(denismakogon): disabled until # https://github.com/iron-io/functions/issues/275 -class TasksV1Controller(controllers.ServiceControllerBase): +class TasksV1Controller(controller.ServiceController): controller_name = "tasks" version = "v1" @@ -33,7 +35,7 @@ class TasksV1Controller(controllers.ServiceControllerBase): # - on each request check if route is public our private # * reject with 401 if route is private # * accept with 200 if route is public - @controllers.api_action( + @requests.api_action( method='GET', route='{project_id}/tasks') async def list(self, request, **kwargs): """ @@ -63,7 +65,7 @@ class TasksV1Controller(controllers.ServiceControllerBase): } }, status=405) - @controllers.api_action( + @requests.api_action( method='GET', route='{project_id}/tasks/{task}') async def show(self, request, **kwargs): """ diff --git a/laos/api/views/app.py b/laos/api/views/app.py index 3641aea..8a61e98 100644 --- a/laos/api/views/app.py +++ b/laos/api/views/app.py @@ -45,11 +45,11 @@ class AppRouteView(object): view = [] for route in self.routes: if not route.is_public: - path = ("{}/private/{}/{}{}".format( + path = ("{}/v1/r/{}/{}{}".format( self.api_url, self.project_id, route.appname, route.path)) else: - path = ("{}/public/{}{}".format( + path = ("{}/r/{}{}".format( self.api_url, route.appname, route.path)) view.append({ "path": path, diff --git a/laos/common/base/__init__.py b/laos/common/base/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/laos/common/base/controllers.py b/laos/common/base/controllers.py deleted file mode 100644 index 4ec4ed6..0000000 --- a/laos/common/base/controllers.py +++ /dev/null @@ -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 diff --git a/laos/common/base/service.py b/laos/common/base/service.py deleted file mode 100644 index 0fa96ce..0000000 --- a/laos/common/base/service.py +++ /dev/null @@ -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) diff --git a/laos/common/logger.py b/laos/common/logger.py index ec997b4..1a399cb 100644 --- a/laos/common/logger.py +++ b/laos/common/logger.py @@ -21,7 +21,7 @@ from laos.common import utils def common_logger_setup( level=logging.DEBUG, - filename='/tmp/aiorchestra.log', + filename='/tmp/laos-api.log', log_formatter='[%(asctime)s] - ' '%(name)s - ' '%(levelname)s - ' diff --git a/laos/service/laos_api.py b/laos/service/laos_api.py index effa89b..c17ddd5 100644 --- a/laos/service/laos_api.py +++ b/laos/service/laos_api.py @@ -17,6 +17,8 @@ import asyncio import click import uvloop +from aioservice.http import service + from laos.api.controllers import apps from laos.api.controllers import routes 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 keystone -from laos.common.base import service from laos.common import config from laos.common import logger as log -class API(service.AbstractWebServer): +class API(service.HTTPService): def __init__(self, host: str='0.0.0.0', port: int=10001, loop: asyncio.AbstractEventLoop=asyncio.get_event_loop(), logger=None, 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__( host=host, 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, logger=logger, debug=debug, + subservice_definitions=[ + v1_service, public_runnable_service + ] ) @@ -126,8 +128,15 @@ def server(host, port, db_uri, event_loop=loop, ) - API(host=host, port=port, loop=loop, - logger=logger, debug=debug).initialize() + API( + 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__": diff --git a/laos/tests/functional/base.py b/laos/tests/functional/base.py index 7237049..9bb28aa 100644 --- a/laos/tests/functional/base.py +++ b/laos/tests/functional/base.py @@ -17,13 +17,14 @@ import os import testtools import uuid +from aioservice.http import service + from laos.api.controllers import apps from laos.api.controllers import routes from laos.api.controllers import runnable from laos.api.controllers import tasks from laos.api.middleware import content_type -from laos.common.base import service from laos.common import config from laos.tests.common import base @@ -36,34 +37,30 @@ class LaosFunctionalTestsBase(base.LaosTestsBase, testtools.TestCase): def setUp(self): self.testloop, logger = self.get_loop_and_logger("functional") - self.testapp = service.AbstractWebServer( - host="localhost", + v1_service = service.VersionedService( + [ + 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, - 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, logger=logger, debug=True, - ).root_service + ).root connection_pool = config.Connection( os.getenv("TEST_DB_URI"), loop=self.testloop) diff --git a/laos/tests/integration/base.py b/laos/tests/integration/base.py index 145daad..1efb2e8 100644 --- a/laos/tests/integration/base.py +++ b/laos/tests/integration/base.py @@ -60,7 +60,7 @@ class LaosIntegrationTestsBase(base.LaosTestsBase, testtools.TestCase): loop=self.testloop, logger=logger, debug=True) 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 }) self.testloop.run_until_complete( diff --git a/requirements.txt b/requirements.txt index a6ecafa..865e4aa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,11 +3,15 @@ # process, which may cause wedges in the gate later. 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 alembic==0.8.8 # MIT click==6.6 # Apache-2.0 + +# IronFunctions python-functionsclient==0.0.1 + +# OpenStack keystoneauth1==2.15.0 # Apache-2.0 python-keystoneclient==3.6.0 # Apache-2.0 diff --git a/setup.py b/setup.py index 68aa679..6a27e37 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ setuptools.setup( packages=setuptools.find_packages(), install_requires=[ "uvloop==0.6.0", - "aiohttp==1.1.5", + "aioservice==0.0.1", "aiomysql==0.0.9", "alembic==0.8.8", "click==6.6",