Refactoring

- enable public/private routes exectuion
	- implementing new DB model - routes
	- refactoring service model to allow sub-apps
	- examples/hello-lambda.sh was changed to reflect new routes API
This commit is contained in:
Denis Makogon 2016-11-16 18:27:02 +02:00
parent 4471a4081a
commit 68b54f28bb
19 changed files with 312 additions and 124 deletions

1
.gitignore vendored
View File

@ -13,3 +13,4 @@ python-troveclient.iml
# Files created by releasenotes build
releasenotes/build
.coverage.*
*.json

View File

@ -123,12 +123,14 @@ In [examples](examples/) folder you can find a script that examines available AP
* `LAOS_API_URL` - Project LaOS API endpoint
* `OS_AUTH_URL` - OpenStack Auth URL
* `OS_PROJECT_ID` - it can be found in OpenStack Dashboard or in CLI
Along with that, you need to adjust [token_request.json](examples/token_request.json) in order to retrieve X-Auth-Token for further authentication against OpenStack and LaOS API service.
* `OS_USERNAME` - OpenStack project-aligned username
* `OS_PASSWORD` - OpenStack project-aligned user password
* `OS_DOMAIN` - OpenStack project domain name
* `OS_PROJECT_NAME` - OpenStack project name
Then just run script:
OS_AUTH_URL=http://192.168.0.112:5000/v3 OS_PROJECT_ID=8fb76785313a4500ac5367eb44a31677 ./hello-lambda.sh
OS_AUTH_URL=http://192.168.0.112:5000/v3 OS_PROJECT_ID=8fb76785313a4500ac5367eb44a31677 OS_USERNAME=admin OS_PASSWORD=root OS_DOMAIN=default OS_PROJECT_NAME=admin ./examples/hello-lambda.sh
Please note, that given values are project-specific, so they can't be reused.

View File

@ -1,18 +1,47 @@
#!/usr/bin/env bash
set +x
set +e
export LAOS_API_URL=${LAOS_API_URL:-http://localhost:10001}
export OS_AUTH_URL=${OS_AUTH_URL:-http://localhost:5000/v3}
export OS_USERNAME=${OS_USERNAME:-admin}
export OS_PASSOWRD=${OS_PASSWORD:-root}
export OS_DOMAIN=${OS_DOMAIN:-default}
export OS_PROJECT_ID=${OS_PROJECT_ID:-"dummy_project_id"}
export OS_TOKEN=`curl -si -d @token_request.json -H "Content-type: application/json" ${OS_AUTH_URL}/auth/tokens | awk '/X-Subject-Token/ {print $2}'`
rm -fr examples/token_request.json
echo -e "{
\"auth\": {
\"identity\": {
\"methods\": [\"password\"],
\"password\": {
\"user\": {
\"name\": \"${OS_USERNAME:-admin}\",
\"domain\": { \"id\": \"${OS_DOMAIN:-default}\" },
\"password\": \"${OS_PASSWORD:-root}\"
}
}
},
\"scope\": {
\"project\": {
\"name\": \"${OS_PROJECT_NAME:-admin}\",
\"domain\": {\"id\": \"${OS_DOMAIN:-default}\" }
}
}
}
}" >> examples/token_request.json
export OS_TOKEN=`curl -si -d @examples/token_request.json -H "Content-type: application/json" ${OS_AUTH_URL}/auth/tokens | awk '/X-Subject-Token/ {print $2}'`
echo -e "Listing apps\n"
curl ${LAOS_API_URL}/v1/${OS_PROJECT_ID}/apps -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool
echo -e "Creating app\n"
curl -X POST -d '{"app":{"name": "testapp"}}' ${LAOS_API_URL}/v1/${PROJECT_ID}/apps -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool
curl -X POST -d '{"app":{"name": "testapp"}}' ${LAOS_API_URL}/v1/${OS_PROJECT_ID}/apps -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool
echo -e "Listing apps\n"
curl ${LAOS_API_URL}/v1/${OS_PROJECT_ID}/apps -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool
@ -20,32 +49,48 @@ curl ${LAOS_API_URL}/v1/${OS_PROJECT_ID}/apps -H "X-Auth-Token:${OS_TOKEN}" -H "
echo -e "Showing app info\n"
export raw_app_info=`curl localhost:10001/v1/${OS_PROJECT_ID}/apps -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool | grep name | awk '{print $2}'`
export app_name=${raw_app_info:1:30}
unset $raw_app_info
curl ${LAOS_API_URL}/v1/${OS_PROJECT_ID}/apps/${app_name} -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool
echo -e "Listing app routes\n"
curl ${LAOS_API_URL}/v1/${PROJECT_ID}/apps/${APP_NAME}/routes -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 -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool
echo -e "Creating app sync route\n"
curl -X POST -d '{"route":{"type": "sync", "path": "/hello-sync", "image": "iron/hello"}}' ${LAOS_API_URL}/v1/${PROJECT_ID}/apps/${APP_NAME}/route -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json"
echo -e "Creating app sync private route\n"
curl -X POST -d '{"route":{"type": "sync", "path": "/hello-sync-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 "Creating app sync public route\n"
curl -X POST -d '{"route":{"type": "sync", "path": "/hello-sync-public", "image": "iron/hello", "is_public": "true" }}' ${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 "Listing app routes\n"
curl ${LAOS_API_URL}/v1/${PROJECT_ID}/apps/${APP_NAME}/routes -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 -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool
echo -e "Show app route\n"
curl ${LAOS_API_URL}/v1/${PROJECT_ID}/apps/${APP_NAME}/routes/hello-sync -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool
echo -e "Show app private route\n"
curl ${LAOS_API_URL}/v1/${OS_PROJECT_ID}/apps/${app_name}/routes/hello-sync-private -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool
echo -e "Running app sync route\n"
curl -X POST -d '{"name": "Johnny"}' ${LAOS_API_URL}/r/${PROJECT_ID}/${APP_NAME}/hello-sync -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool
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 "Deleting app route\n"
curl -X DELETE ${LAOS_API_URL}/v1/${PROJECT_ID}/apps/${APP_NAME}/routes/hello_sync -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
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
echo -e "Creating app async route\n"
curl -X POST -d '{"route":{"type": "async", "path": "/hello-async", "image": "iron/hello"}}' ${LAOS_API_URL}/v1/${PROJECT_ID}/apps/${APP_NAME}/route -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"
curl -X POST -d '{"name": "Johnny"}' ${LAOS_API_URL}/r/${PROJECT_ID}/${APP_NAME}/hello-async -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool
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
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-private -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-async-private -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool
echo -e "Listing app routes\n"
curl ${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 "Deleting app\n"
curl -X DELETE ${LAOS_API_URL}/v1/${OS_PROJECT_ID}/apps/${app_name} -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool
echo -e "Listing apps\n"
curl ${LAOS_API_URL}/v1/${OS_PROJECT_ID}/apps -H "X-Auth-Token:${OS_TOKEN}" -H "Content-Type: application/json" | python3 -mjson.tool

View File

@ -1,20 +0,0 @@
{
"auth": {
"identity": {
"methods": ["password"],
"password": {
"user": {
"name": "admin",
"domain": { "id": "default" },
"password": "root"
}
}
},
"scope": {
"project": {
"name": "admin",
"domain": { "id": "default" }
}
}
}
}

View File

@ -12,6 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
import json
from aiohttp import web
from laos.api.views import app as app_view
@ -34,6 +36,10 @@ class AppRouteV1Controller(controllers.ServiceControllerBase):
log, fnclient = c.logger, c.functions_client
project_id = request.match_info.get('project_id')
app = request.match_info.get('app')
log.info("Listing app {} routes for project: {}."
.format(app, project_id))
if not (await app_model.Apps.exists(app, project_id)):
return web.json_response(data={
"error": {
@ -44,10 +50,20 @@ class AppRouteV1Controller(controllers.ServiceControllerBase):
fn_app_routes = (await (await fnclient.apps.show(
app, loop=c.event_loop)).routes.list(loop=c.event_loop))
for fn_route in fn_app_routes:
stored_route = (await app_model.Routes.find_by(
app_name=app,
project_id=project_id,
path=fn_route.path)).pop()
setattr(fn_route, "is_public", stored_route.public)
api_url = "{}://{}".format(request.scheme, request.host)
log.info("Listing app {} routes for project: {}."
.format(app, project_id))
return web.json_response(data={
"routes": app_view.AppRouteView(fn_app_routes).view(),
"routes": app_view.AppRouteView(api_url,
project_id,
fn_app_routes).view(),
"message": "Successfully loaded app routes",
}, status=200)
@ -58,6 +74,7 @@ class AppRouteV1Controller(controllers.ServiceControllerBase):
log, fnclient = c.logger, c.functions_client
project_id = request.match_info.get('project_id')
app = request.match_info.get('app')
if not (await app_model.Apps.exists(app, project_id)):
return web.json_response(data={
"error": {
@ -67,6 +84,8 @@ class AppRouteV1Controller(controllers.ServiceControllerBase):
data = (await request.json())['route']
path = data['path']
is_public = json.loads(data.get(
'is_public', "false"))
try:
fn_app = await fnclient.apps.show(app, loop=c.event_loop)
@ -95,15 +114,26 @@ class AppRouteV1Controller(controllers.ServiceControllerBase):
}
}, status=getattr(ex, "status", 500))
new_fn_route = (await (await fnclient.apps.show(
app, loop=c.event_loop)).routes.create(
new_fn_route = (await fn_app.routes.create(
**data, loop=c.event_loop))
stored_route = await app_model.Routes(
app_name=new_fn_route.appname,
project_id=project_id,
path=new_fn_route.path,
is_public=is_public).save()
log.info("Creating new route in app {} "
"for project: {} with data {}"
.format(app, project_id, str(data)))
api_url = "{}://{}".format(request.scheme, request.host)
setattr(new_fn_route, "is_public", stored_route.public)
view = app_view.AppRouteView(
api_url, project_id, [new_fn_route]).view()
return web.json_response(data={
"route": app_view.AppRouteView([new_fn_route]).view(),
"route": view,
"message": "App route successfully created"
}, status=200)
@ -116,6 +146,13 @@ class AppRouteV1Controller(controllers.ServiceControllerBase):
app = request.match_info.get('app')
path = request.match_info.get('route')
if not (await app_model.Apps.exists(app, project_id)):
return web.json_response(data={
"error": {
"message": "App {0} not found".format(app),
}
}, status=404)
try:
fn_app = await fnclient.apps.show(app, loop=c.event_loop)
route = await fn_app.routes.show(
@ -129,8 +166,20 @@ class AppRouteV1Controller(controllers.ServiceControllerBase):
log.info("Requesting route {} in app {} for project: {}"
.format(path, app, project_id))
api_url = "{}://{}".format(request.scheme, request.host)
stored_route = (await app_model.Routes.find_by(
app_name=app,
project_id=project_id,
path=route.path)).pop()
setattr(route, "is_public", stored_route.public)
return web.json_response(data={
"route": app_view.AppRouteView([route]).view().pop(),
"route": app_view.AppRouteView(api_url,
project_id,
[route]).view().pop(),
"message": "App route successfully loaded"
}, status=200)
@ -144,10 +193,19 @@ class AppRouteV1Controller(controllers.ServiceControllerBase):
path = request.match_info.get('route')
log.info("Deleting route {} in app {} for project: {}"
.format(path, app, project_id))
if not (await app_model.Apps.exists(app, project_id)):
return web.json_response(data={
"error": {
"message": "App {0} not found".format(app),
}
}, status=404)
try:
fn_app = await fnclient.apps.show(app, loop=c.event_loop)
await fn_app.routes.show("/{}".format(path), loop=c.event_loop)
await fn_app.routes.delete("/{}".format(path), loop=c.event_loop)
await app_model.Routes.delete(project_id=project_id, app_name=app)
except Exception as ex:
return web.json_response(data={
"error": {

View File

@ -18,14 +18,8 @@ from laos.common.base import controllers
from laos.common import config
class RunnableV1Controller(controllers.ServiceControllerBase):
class RunnableMixin(object):
controller_name = "runnable"
# IronFunction uses `r` as runnable instead API version
version = "r"
@controllers.api_action(
method='POST', route='{project_id}/{app}/{route}')
async def run(self, request, **kwargs):
c = config.Config.config_instance()
fnclient = c.functions_client
@ -68,3 +62,31 @@ class RunnableV1Controller(controllers.ServiceControllerBase):
return _data
return web.json_response(status=200, data=process_result(result))
class PublicRunnableV1Controller(controllers.ServiceControllerBase,
RunnableMixin):
controller_name = "public_runnable"
# IronFunction uses `r` as runnable instead API version
version = "r"
@controllers.api_action(
method='POST', route='{app}/{route}')
async def run(self, request, **kwargs):
return await super(PublicRunnableV1Controller,
self).run(request, **kwargs)
class RunnableV1Controller(controllers.ServiceControllerBase,
RunnableMixin):
controller_name = "runnable"
# IronFunction uses `r` as runnable instead API version
version = "r"
@controllers.api_action(
method='POST', route='{project_id}/{app}/{route}')
async def run(self, request, **kwargs):
return await super(RunnableV1Controller,
self).run(request, **kwargs)

View File

@ -27,6 +27,12 @@ class TasksV1Controller(controllers.ServiceControllerBase):
controller_name = "tasks"
version = "v1"
# TODO(denismakogon):
# - define subapp to process requests to tasks API:
# * extract tasks V1 controller to subapp
# - 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(
method='GET', route='{project_id}/tasks')
async def get(self, request, **kwargs):

View File

@ -18,12 +18,13 @@ async def content_type_validator(app: web.Application, handler):
async def middleware_handler(request: web.Request):
headers = request.headers
content_type = headers.get("Content-Type")
if "application/json" not in content_type:
return web.json_response(
data={
"error": {
"message": "Invalid content type"
}
}, status=400)
if request.has_body:
if "application/json" != content_type:
return web.json_response(
data={
"error": {
"message": "Invalid content type"
}
}, status=400)
return await handler(request)
return middleware_handler

View File

@ -33,16 +33,26 @@ class AppView(object):
class AppRouteView(object):
def __init__(self, fn_app_routes):
def __init__(self, api_url, project_id, fn_app_routes):
self.routes = fn_app_routes
self.api_url = api_url
self.project_id = project_id
def view(self):
view = []
for route in self.routes:
if not route.is_public:
path = ("{}/private/{}/{}{}".format(
self.api_url, self.project_id,
route.appname, route.path))
else:
path = ("{}/public/{}{}".format(
self.api_url, route.appname, route.path))
view.append({
"path": route.path,
"path": path,
"type": route.type,
"memory": route.memory,
"image": route.image,
"is_public": route.is_public,
})
return view

View File

@ -35,14 +35,13 @@ class ServiceControllerBase(object):
method.arg_method,
method.arg_route] for method in methods]
def __init__(self, service: web.Application):
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)
service.router.add_route(http_method,
"/{}/{}".format(self.version, route),
getattr(self, proxy_fn),
name=proxy_fn)
sub_service.router.add_route(
http_method, "/{}".format(route),
getattr(self, proxy_fn), name=proxy_fn)
def api_action(**outter_kwargs):

View File

@ -13,11 +13,8 @@
# under the License.
import asyncio
import os
import typing
from aiohttp import web
from laos.common.base import controllers as c
from laos.common import logger as log
@ -25,49 +22,61 @@ class AbstractWebServer(object):
def __init__(self, host: str='127.0.0.1',
port: int= '10001',
controllers: typing.List[c.ServiceControllerBase]=None,
middlewares: list=None,
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__)):
level="INFO").setup_logger(__name__),
debug=False):
"""
HTTP server abstraction class
:param host:
:param port:
:param controllers:
:param middlewares:
:param event_loop:
:param logger:
: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.controllers = controllers
self.event_loop = event_loop
self.service = web.Application(
loop=self.event_loop,
debug=os.environ.get('PYTHONASYNCIODEBUG', 0),
middlewares=middlewares if middlewares else [])
self.service_handler = None
self.server = None
self.logger = logger
def _apply_routers(self):
if self.controllers:
for controller in self.controllers:
controller(self.service)
self.root_service = web.Application(
logger=self.logger,
loop=self.event_loop,
debug=debug
)
def shutdown(self):
self.server.close()
self.event_loop.run_until_complete(self.server.wait_closed())
self.event_loop.run_until_complete(
self.service_handler.finish_connections(1.0))
self.event_loop.run_until_complete(self.service.cleanup())
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):
self._apply_routers()
try:
web.run_app(self.service, host=self.host, port=self.port,
shutdown_timeout=10, access_log=self.logger)
except KeyboardInterrupt:
self.shutdown()
web.run_app(self.root_service, host=self.host, port=self.port,
shutdown_timeout=10, access_log=self.logger)

View File

@ -76,7 +76,10 @@ class UnifiedLogger(object, metaclass=utils.Singleton):
if 'DEBUG' not in level:
self.log_formatter = (
'[%(asctime)s] - '
'%(message)s')
'%(name)s - '
'%(module)s.py:%(lineno)d - '
'%(message)s'
)
else:
self. log_formatter = (
'[%(asctime)s] - '

View File

@ -27,6 +27,10 @@ class BaseDatabaseModel(object):
DELETE = "DELETE FROM {} {}"
def __init__(self, **kwargs):
logger = config.Config.config_instance().logger
logger.info("Attempting to create object class instance "
"'{}' with attributes '{}'"
.format(str(self.__class__), str(kwargs)))
self.id = uuid.uuid4().hex
self.created_at = str(datetime.datetime.now())
self.updated_at = str(datetime.datetime.now())
@ -34,30 +38,37 @@ class BaseDatabaseModel(object):
setattr(self, k, v)
async def save(self):
logger = config.Config.config_instance().logger
insert = self.INSERT.format(
self.table_name,
str(tuple([getattr(self, clmn) for clmn in self.columns]))
)
print(insert)
logger.info("Attempting to save object '{}' "
"using SQL query: {}".format(self.table_name, insert))
async with config.Connection.from_class().acquire() as conn:
async with conn.cursor() as cur:
await cur.execute(insert)
await conn.commit()
logger.info("Object saved.")
return self
@classmethod
async def delete(cls, **kwargs):
logger = config.Config.config_instance().logger
delete = cls.DELETE.format(
cls.table_name, cls.__define_where(**kwargs))
logger.info("Attempting to delete object '{}' "
"using SQL query: {}".format(cls.table_name, delete))
async with config.Connection.from_class().acquire() as conn:
async with conn.cursor() as cur:
await cur.execute(delete)
await conn.commit()
logger.info("Object gone.")
async def update(self, **kwargs):
async with config.Connection.from_class().acquire() as conn:
async with conn.cursor() as cur:
await cur.execute()
# async def update(self, **kwargs):
# async with config.Connection.from_class().acquire() as conn:
# async with conn.cursor() as cur:
# await cur.execute()
@classmethod
async def exists(cls, name, project_id):
@ -81,12 +92,15 @@ class BaseDatabaseModel(object):
@classmethod
async def find_by(cls, **kwargs):
logger = config.Config.config_instance().logger
where = cls.__define_where(**kwargs)
select = cls.SELECT.format(
cls.table_name, where)
logger.info("Attempting to find object(s) '{}' "
"using SQL : {}".format(cls.table_name, select))
async with config.Connection.from_class().acquire() as conn:
async with conn.cursor() as cur:
await cur.execute(cls.SELECT.format(
cls.table_name, where))
await cur.execute(select)
results = await cur.fetchall()
return [cls.from_tuple(instance)
for instance in results] if results else []

View File

@ -26,3 +26,20 @@ class Apps(persistence.BaseDatabaseModel):
"updated_at",
"name"
)
class Routes(persistence.BaseDatabaseModel):
table_name = "routes"
columns = (
"project_id",
"path",
"is_public",
"app_name",
"created_at",
"updated_at",
)
@property
def public(self):
return True if self.is_public else False

View File

@ -35,22 +35,36 @@ class API(service.AbstractWebServer):
def __init__(self, host: str='0.0.0.0',
port: int=10001,
loop: asyncio.AbstractEventLoop=asyncio.get_event_loop(),
logger=None):
logger=None,
debug=False):
super(API, self).__init__(
host=host,
port=port,
controllers=[
apps.AppV1Controller,
routes.AppRouteV1Controller,
runnable.RunnableV1Controller,
tasks.TasksV1Controller,
],
middlewares=[
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,
)
@ -73,6 +87,7 @@ class API(service.AbstractWebServer):
help='Logging file')
@click.option('--log-file', default=None,
help='Log file path')
@click.option('--debug', default=False, is_flag=True)
def server(host, port, db_uri,
keystone_endpoint,
functions_host,
@ -81,6 +96,7 @@ def server(host, port, db_uri,
functions_api_protocol,
log_level,
log_file,
debug,
):
"""
Starts an Project Laos API service
@ -88,7 +104,7 @@ def server(host, port, db_uri,
logger = log.UnifiedLogger(
log_to_console=True if not log_file else False,
filename=None if not log_file else log_file,
level=log_level).setup_logger(__name__)
level=log_level).setup_logger(__package__)
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
loop = asyncio.get_event_loop()
@ -104,17 +120,14 @@ def server(host, port, db_uri,
config.Config(
auth_url=keystone_endpoint,
functions_host=functions_host,
functions_port=functions_port,
functions_api_protocol=functions_api_protocol,
functions_api_version=functions_api_version,
functions_client=fnclient,
logger=logger,
connection=conn,
event_loop=loop,
)
API(host=host, port=port, loop=loop, logger=logger).initialize()
API(host=host, port=port, loop=loop,
logger=logger, debug=debug).initialize()
if __name__ == "__main__":

0
laos/tests/__init__.py Normal file
View File

View File

@ -27,7 +27,17 @@ def upgrade():
sa.Column('updated_at', sa.String(255)),
sa.Column('name', sa.String(255), nullable=False, primary_key=True),
)
op.create_table(
'routes',
sa.Column('project_id', sa.String(255), nullable=False),
sa.Column('path', sa.String(255), nullable=False, primary_key=True),
sa.Column('is_public', sa.Boolean(create_constraint=False), nullable=False),
sa.Column('app_name', sa.String(255), nullable=False, primary_key=True),
sa.Column('created_at', sa.String(255), nullable=False),
sa.Column('updated_at', sa.String(255), nullable=False),
)
def downgrade():
op.drop_table('routes')
op.drop_table('apps')

View File

@ -9,4 +9,3 @@ alembic>=0.8.4 # MIT
click
keystoneauth1>=2.14.0 # Apache-2.0
python-keystoneclient==3.6.0
SQLAlchemy<1.1.0,>=1.0.10 # MIT

View File

@ -36,7 +36,6 @@ setuptools.setup(
"click",
"keystoneauth1>=2.14.0",
"python-keystoneclient==3.6.0",
"SQLAlchemy<1.1.0,>=1.0.10",
],
license='License :: OSI Approved :: Apache Software License',
classifiers=[