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:
parent
4471a4081a
commit
68b54f28bb
1
.gitignore
vendored
1
.gitignore
vendored
@ -13,3 +13,4 @@ python-troveclient.iml
|
||||
# Files created by releasenotes build
|
||||
releasenotes/build
|
||||
.coverage.*
|
||||
*.json
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -1,20 +0,0 @@
|
||||
{
|
||||
"auth": {
|
||||
"identity": {
|
||||
"methods": ["password"],
|
||||
"password": {
|
||||
"user": {
|
||||
"name": "admin",
|
||||
"domain": { "id": "default" },
|
||||
"password": "root"
|
||||
}
|
||||
}
|
||||
},
|
||||
"scope": {
|
||||
"project": {
|
||||
"name": "admin",
|
||||
"domain": { "id": "default" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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": {
|
||||
|
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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] - '
|
||||
|
@ -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 []
|
||||
|
@ -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
|
||||
|
@ -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
0
laos/tests/__init__.py
Normal 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')
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user