Prepare for project renaming

- refactoring with relative imports to support simple renaming
 - new tox job to build and simply test code inside docker container
This commit is contained in:
Denis Makogon 2016-12-02 18:47:58 +02:00
parent e02b003fcb
commit a6a8405510
27 changed files with 178 additions and 154 deletions

View File

@ -11,5 +11,5 @@ RUN pip3 install -r /code/requirements.txt
RUN python3 /code/setup.py install RUN python3 /code/setup.py install
ENTRYPOINT ["python3", "/code/laos/service/laos_api.py"] ENTRYPOINT ["python3", "/code/service/laos_api.py"]
EXPOSE 10001 EXPOSE 10001

View File

@ -1,6 +1,6 @@
LAOS_HOST=0.0.0.0 LAOS_HOST=0.0.0.0
LAOS_PORT=10001 LAOS_PORT=10001
LAOS_DB=mysql://root:ubuntu@192.168.0.120/functions LAOS_DB=mysql+pymysql://root:ubuntu@192.168.0.120/functions
KEYSTONE_ENDPOINT=http://192.168.0.120:5000/v3 KEYSTONE_ENDPOINT=http://192.168.0.120:5000/v3
FUNCTIONS_URL=http://192.168.0.120:8080/v1 FUNCTIONS_URL=http://192.168.0.120:8080/v1
LAOS_LOG_LEVEL=INFO LAOS_LOG_LEVEL=INFO

105
README.md
View File

@ -168,111 +168,6 @@ Once server is launched you can navigate to:
to see recent API docs to see recent API docs
Testing (general information)
-----------------------------
In order to run tests you need to install `Tox`:
$ pip install tox
Also, you will need a running MySQL instance with the database migrations applied from the previous step.
Tests are dependent on pre-created MySQL database for persistence.
Please set env var
$ export TEST_DB_URI=mysql://<your-user>:<your-user-password>@<mysql-host>:<mysql-port>/<functions-db>
Testing: PEP8
-------------
In order to run `PEP8` style checks run following command:
$ tox -e pep8
Testing: Functional
-------------------
In order to run `functional` tests run following command:
$ tox -e py35-functional
Pros:
* lightweight (controllers and DB models testing)
* no OpenStack required
* no IronFunctions required
Cons:
* MySQL server required
* OpenStack authentication is not tested
* IronFunctions API stubbed with fake implementation
Testing: Integration
--------------------
Integration tests are dependent on following env variables:
* TEST_DB_URI - similar to functional tests, database endpoint
* FUNCTIONS_API_URL - IronFunctions API URL (default value - `http://localhost:8080/v1`)
* OS_AUTH_URL - OpenStack Identity endpoint
* OS_PROJECT_NAME - OpenStack user-specific project name
* OS_USERNAME - OpenStack user name
* OS_PASSWORD - OpenStack user user password
To run tests use following command:
export TEST_DB_URI=mysql://<your-user>:<your-user-password>@<mysql-host>:<mysql-port>/<functions-db>
export FUNCTIONS_API_URL=<functions-api-protocol>://<functions-host>:<functions-port>/<functions-api-version>
export OS_AUTH_URL=<identity-api-protocol>://<identity-host>:<identity-port>/<identity-api-version>
export OS_PROJECT_NAME=<project-name>
export OS_USERNAME=<project-name>
export OS_PASSWORD=<project-name>
tox -epy35-integration
Testing: Coverage regression
----------------------------
In order to build quality software it is necessary to keep test coverage at its highest point.
So, as part of `Tox` testing new check was added - functional test coverage regression.
In order to run it use following command:
$ tox -e py35-functional-regression
3rd party bugs to resolve
-------------------------
IronFunctions:
* https://github.com/iron-io/functions/issues/298
* https://github.com/iron-io/functions/issues/296
* https://github.com/iron-io/functions/issues/275
* https://github.com/iron-io/functions/issues/274
TODOs
-----
Swagger doc:
* Make swagger doc more explicit on HTTP POST/UPDATE body content
* HTTP headers requests
IronFunctions:
* Support app deletion in IronFunctions
* Support tasks listing/showing
Laos:
* Tests: integration, functional, units
* Better logging coverage
Python Functions client:
* Support logging instance passing in [function-python](https://github.com/iron-io/functions_python)
* python-laosclient (ReST API client and CLI tool)
* App writing examples
Contacts Contacts
-------- --------

View File

@ -17,9 +17,9 @@ from aiohttp import web
from aioservice.http import controller from aioservice.http import controller
from aioservice.http import requests from aioservice.http import requests
from laos.api.views import app as app_view from ...common import config
from laos.common import config from ...models import app as app_model
from laos.models import app as app_model from ..views import app as app_view
class AppV1Controller(controller.ServiceController): class AppV1Controller(controller.ServiceController):
@ -202,12 +202,12 @@ class AppV1Controller(controller.ServiceController):
stored_app = (await app_model.Apps.find_by( stored_app = (await app_model.Apps.find_by(
project_id=project_id, name=app_name)).pop() project_id=project_id, name=app_name)).pop()
c.logger.info("Updating an app {} for project: {} with data {}" c.logger.info("Updating app {} for project: {} with data {}"
.format(app_name, project_id, str(data))) .format(app_name, project_id, str(data)))
return web.json_response( return web.json_response(
data={ data={
"app": app_view.AppView(stored_app, fn_app).view(), "app": app_view.AppView(stored_app, fn_app).view(),
"message": "App successfully update" "message": "App successfully updated"
}, },
status=200 status=200
) )

View File

@ -19,9 +19,9 @@ from aiohttp import web
from aioservice.http import controller from aioservice.http import controller
from aioservice.http import requests from aioservice.http import requests
from laos.api.views import app as app_view from ...common import config
from laos.common import config from ...models import app as app_model
from laos.models import app as app_model from ..views import app as app_view
class AppRouteV1Controller(controller.ServiceController): class AppRouteV1Controller(controller.ServiceController):

View File

@ -17,7 +17,7 @@ from aiohttp import web
from aioservice.http import controller from aioservice.http import controller
from aioservice.http import requests from aioservice.http import requests
from laos.common import config from ...common import config
class RunnableMixin(object): class RunnableMixin(object):

View File

@ -14,12 +14,12 @@
from aiohttp import web from aiohttp import web
# from laos.models import app as app_model # from ...models import app as app_model
from aioservice.http import controller from aioservice.http import controller
from aioservice.http import requests from aioservice.http import requests
# from laos.common import config # from ...common import config
# TODO(denismakogon): disabled until # TODO(denismakogon): disabled until

View File

@ -18,7 +18,7 @@ from keystoneclient import client
from aiohttp import web from aiohttp import web
from laos.common import config from ...common import config
async def auth_through_token(app: web.Application, handler): async def auth_through_token(app: web.Application, handler):

View File

@ -16,7 +16,7 @@
import aiomysql import aiomysql
import asyncio import asyncio
from laos.common import utils from . import utils
from functionsclient.v1 import client from functionsclient.v1 import client

View File

@ -16,7 +16,7 @@ import datetime
import logging import logging
import sys import sys
from laos.common import utils from . import utils
def common_logger_setup( def common_logger_setup(

View File

@ -15,7 +15,7 @@
import datetime import datetime
import uuid import uuid
from laos.common import config from . import config
class BaseDatabaseModel(object): class BaseDatabaseModel(object):

View File

@ -12,7 +12,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from laos.common import persistence from ..common import persistence
class Apps(persistence.BaseDatabaseModel): class Apps(persistence.BaseDatabaseModel):

View File

@ -16,7 +16,7 @@ import asyncio
import datetime import datetime
import uvloop import uvloop
from laos.common import logger as log from ...common import logger as log
class LaosTestsBase(object): class LaosTestsBase(object):

View File

@ -120,7 +120,7 @@ class FakeApps(object):
async def delete(self, app_name, loop=None): async def delete(self, app_name, loop=None):
if app_name not in APPS: if app_name not in APPS:
raise client.FunctionsAPIException( raise client.FunctionsAPIException(
"App {} not exist.".format(app_name), 404) "App {} not found.".format(app_name), 404)
else: else:
if APP_ROUTES[app_name]: if APP_ROUTES[app_name]:
raise client.FunctionsAPIException( raise client.FunctionsAPIException(

View File

@ -18,17 +18,17 @@ import uuid
from aioservice.http import service from aioservice.http import service
from laos.api.controllers import apps from ...api.controllers import apps
from laos.api.controllers import routes from ...api.controllers import routes
from laos.api.controllers import runnable from ...api.controllers import runnable
from laos.api.controllers import tasks from ...api.controllers import tasks
from laos.api.middleware import content_type from ...api.middleware import content_type
from laos.common import config from ...common import config
from laos.tests.common import base from ..common import base
from laos.tests.common import client from ..common import client
from laos.tests.fakes import functions_api from ..fakes import functions_api
class LaosFunctionalTestsBase(base.LaosTestsBase, testtools.TestCase): class LaosFunctionalTestsBase(base.LaosTestsBase, testtools.TestCase):

View File

@ -12,8 +12,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from laos.tests.common import apps as apps_suite from ..common import apps as apps_suite
from laos.tests.functional import base from ..functional import base
class TestApps(base.LaosFunctionalTestsBase, apps_suite.AppsTestSuite): class TestApps(base.LaosFunctionalTestsBase, apps_suite.AppsTestSuite):

View File

@ -12,8 +12,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from laos.tests.common import routes as routes_suite from ..common import routes as routes_suite
from laos.tests.functional import base from ..functional import base
class TestAppRoutes(base.LaosFunctionalTestsBase, class TestAppRoutes(base.LaosFunctionalTestsBase,

View File

@ -12,18 +12,19 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import aiohttp
import json import json
import os import os
from urllib import parse
import aiohttp
import testtools import testtools
from laos.common import config from ...common import config
from laos.service import laos_api from ..common import base
from ..common import client
from laos.tests.common import base
from laos.tests.common import client
from urllib import parse from service import laos_api
class LaosIntegrationTestsBase(base.LaosTestsBase, testtools.TestCase): class LaosIntegrationTestsBase(base.LaosTestsBase, testtools.TestCase):

View File

@ -12,8 +12,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from laos.tests.common import apps as apps_suite from ..common import apps as apps_suite
from laos.tests.integration import base from ..integration import base
class TestIntegrationApps(base.LaosIntegrationTestsBase, class TestIntegrationApps(base.LaosIntegrationTestsBase,

View File

@ -12,8 +12,8 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from laos.tests.common import routes as routes_suite from ..common import routes as routes_suite
from laos.tests.functional import base from ..functional import base
class TestIntegrationAppRoutes(base.LaosFunctionalTestsBase, class TestIntegrationAppRoutes(base.LaosFunctionalTestsBase,

View File

@ -3,7 +3,7 @@
# 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
aioservice==0.0.1 # Apache-2.0 aioservice==0.0.2 # 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

18
scripts/docker_full.sh Executable file
View File

@ -0,0 +1,18 @@
#!/usr/bin/env bash
set +x
set +e
set +i
docker build -t laos-api -f Dockerfile . 2>&1
docker_h=$(echo ${DOCKER_HOST} | tr "://" " " |awk '{print $2}')
echo -e "Docker IP address ${docker_h}"
echo -e "OpenStack Identity service URL ${OS_AUTH_URL}"
echo -e "IronFunctions URL ${FUNCTIONS_API_URL}"
echo -e "Persistent storage URI ${TEST_DB_URI}"
docker run -d -p ${docker_h}:10002:10001 --env LAOS_HOST=0.0.0.0 --env LAOS_PORT=10001 --env LAOS_DB=${TEST_DB_URI} --env KEYSTONE_ENDPOINT=${OS_AUTH_URL} --env FUNCTIONS_URL=${FUNCTIONS_API_URL} --env LAOS_LOG_LEVEL=INFO laos-api
sleep 2
docker ps
echo -e "Service running on ${docker_h}:10002"
curl -X GET http://${docker_h}:10002/api/swagger.json | python -mjson.tool
docker stop -t 1 $(docker ps | grep "${docker_h}:10002" | awk '{print $1}')

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",
"aioservice==0.0.1", "aioservice==0.0.2",
"aiomysql==0.0.9", "aiomysql==0.0.9",
"alembic==0.8.8", "alembic==0.8.8",
"click==6.6", "click==6.6",

107
testing.md Normal file
View File

@ -0,0 +1,107 @@
Testing
-------
In order to run tests you need to install `Tox`:
$ pip install tox
Also, you will need a running MySQL instance with the database migrations applied from the previous step.
Tests are dependent on pre-created MySQL database for persistence.
Please set env var
$ export TEST_DB_URI=mysql://<your-user>:<your-user-password>@<mysql-host>:<mysql-port>/<functions-db>
PEP8 style checks
-----------------
In order to run `PEP8` style checks run following command:
$ tox -e pep8
Functional testing
------------------
In order to run `functional` tests run following command:
$ tox -e py35-functional
Pros:
* lightweight (controllers and DB models testing)
* no OpenStack required
* no IronFunctions required
Cons:
* MySQL server required
* OpenStack authentication is not tested
* IronFunctions API stubbed with fake implementation
Integration integrations
------------------------
Integration tests are dependent on following env variables:
* TEST_DB_URI - similar to functional tests, database endpoint
* FUNCTIONS_API_URL - IronFunctions API URL (default value - `http://localhost:8080/v1`)
* OS_AUTH_URL - OpenStack Identity endpoint
* OS_PROJECT_NAME - OpenStack user-specific project name
* OS_USERNAME - OpenStack user name
* OS_PASSWORD - OpenStack user user password
To run tests use following command:
export TEST_DB_URI=mysql://<your-user>:<your-user-password>@<mysql-host>:<mysql-port>/<functions-db>
export FUNCTIONS_API_URL=<functions-api-protocol>://<functions-host>:<functions-port>/<functions-api-version>
export OS_AUTH_URL=<identity-api-protocol>://<identity-host>:<identity-port>/<identity-api-version>
export OS_PROJECT_NAME=<project-name>
export OS_USERNAME=<project-name>
export OS_PASSWORD=<project-name>
tox -epy35-integration
Testing: Docker-build
---------------------
This type of testing allows to ensure if code can be build inside docker container with no problems.
In order to run this check use following commands::
export DOCKER_HOST=tcp://<docker-host>:<docker-port>>
export TEST_DB_URI=mysql://<your-user>:<your-user-password>@<mysql-host>:<mysql-port>/<functions-db>
export FUNCTIONS_API_URL=<functions-api-protocol>://<functions-host>:<functions-port>/<functions-api-version>
export OS_AUTH_URL=<identity-api-protocol>://<identity-host>:<identity-port>/<identity-api-version>
tox -e docker-build
During this check Tox:
* builds an image
* deletes all artifacts (Python3.5 image and recently built image)
Testing Docker-full
-------------------
This type of testing allows to ensure if code code can be build and run successfully inside docker container with no problems.
In order to run this check use following commands::
export DOCKER_HOST=tcp://<docker-host>:<docker-port>>
export TEST_DB_URI=mysql://<your-user>:<your-user-password>@<mysql-host>:<mysql-port>/<functions-db>
export FUNCTIONS_API_URL=<functions-api-protocol>://<functions-host>:<functions-port>/<functions-api-version>
export OS_AUTH_URL=<identity-api-protocol>://<identity-host>:<identity-port>/<identity-api-version>
tox -e docker-full
During this check following operations are performed::
* build container from source code
* run container with exposed ports
* request Swagger API doc to see if API is responsive
* tear-down running container
Coverage regression testing
---------------------------
In order to build quality software it is necessary to keep test coverage at its highest point.
So, as part of `Tox` testing new check was added - functional test coverage regression.
In order to run it use following command:
$ tox -e py35-functional-regression

11
tox.ini
View File

@ -1,7 +1,7 @@
# Project LaOS # Project LaOS
[tox] [tox]
envlist = py35-functional,py35-functional-regression,py35-integration,py35-integration-regression,pep8 envlist = py35-functional,py35-functional-regression,py35-integration,py35-integration-regression,pep8,docker-build
minversion = 1.6 minversion = 1.6
skipsdist = True skipsdist = True
@ -14,16 +14,16 @@ passenv =
OS_PASSWORD OS_PASSWORD
OS_USERNAME OS_USERNAME
OS_PROJECT_NAME OS_PROJECT_NAME
DOCKER_HOST
setenv = VIRTUAL_ENV={envdir} setenv = VIRTUAL_ENV={envdir}
usedevelop = True usedevelop = True
install_command = pip install -U {opts} {packages} install_command = pip install -U {opts} {packages}
deps = -r{toxinidir}/requirements.txt deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt -r{toxinidir}/test-requirements.txt
commands = find . -type f -name "*.pyc" -delete commands = find . -type f -name "*.pyc" -delete
rm -f .testrepository/times.dbm
python setup.py testr --testr-args='{posargs}'
whitelist_externals = find whitelist_externals = find
rm rm
docker
[testenv:pep8] [testenv:pep8]
commands = flake8 commands = flake8
@ -48,7 +48,10 @@ commands =
rm -rf doc/html doc/build rm -rf doc/html doc/build
python setup.py build_sphinx python setup.py build_sphinx
[testenv:docker-full]
commands = {toxinidir}/scripts/docker_full.sh
[flake8] [flake8]
ignore = H202,H404,H405,H501 ignore = H202,H304,H404,H405,H501
show-source = True show-source = True
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,migrations exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,migrations