Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from piper.services import TestMessageAdder, StringValue, TesseractRecognizer, SpacyNER
# from piper.services import TestMessageAdder
# from piper.services import StringValue
# from piper.services import TesseractRecognizer
# from piper.services import SpacyNER
from piper.services import FaceRecognizer
from piper.envs import CurrentEnv, DockerEnv
from piper.configurations import get_configuration
import time
import asyncio
import sys
from piper.utils import tesrct_utils as tu
# from piper.utils import tesrct_utils as tu

from loguru import logger
logger.add("file.log", level="INFO", backtrace=True, diagnose=True, rotation='5 MB')
Expand All @@ -29,7 +33,9 @@
loop = asyncio.get_event_loop()
with DockerEnv() as env:
# object created
recognizer = TesseractRecognizer(port=cfg.docker_app_port)
# recognizer = TesseractRecognizer(port=cfg.docker_app_port)
recognizer = FaceRecognizer(port=cfg.docker_app_port)


result = loop.run_until_complete(recognizer())
logger.info(f'result of recognition is {result}')
Expand Down
35 changes: 29 additions & 6 deletions piper/base/backend/templates/fast-api.j2
Original file line number Diff line number Diff line change
@@ -1,25 +1,48 @@
import time

from fastapi import FastAPI, Request, status
from piper.envs import CurrentEnv
from fastapi import FastAPI, Request, status, Response, File, UploadFile
from piper.envs import CurrentEnv, DockerEnv
from piper.configurations import get_configuration

from loguru import logger
logger.add("file_.log", level="INFO", backtrace=True, diagnose=True, rotation='5 MB')

{% for script_name in scripts.keys() %}
from {{ script_name }} import *
{% endfor %}

cfg = get_configuration()
app = FastAPI(debug=True)

@app.post('/health_check', status_code = status.HTTP_200_OK)
async def health_check():
logger.info('fast_api.j2 test health_check')
return {"message": "health check"}


logger.debug(f'CurrentEnv is {CurrentEnv}')
logger.debug(f'cfg.env is {cfg.env}')


# with CurrentEnv():
# if True:
# with DockerEnv():
with CurrentEnv():
service = {{ service_class }}( {% for k, v in service_kwargs.items() %} {{ k }}={{ v }}, {% endfor %} )
logger.info(f'CurrentEnv')
service = {{ service_class }}({% for k, v in service_kwargs.items() %} {{ k }}={{ v }}, {% endfor %})
logger.info(f'service {service}')

@app.post('/{{ function_name }}')
async def {{ function_name }}(
request_model: {{ request_model }},
#request_model: {{ request_model }},
file: UploadFile = File(...)
):
result = await service.{{ function_name }}(request_model)
data_b = await file.read()
logger.info('{{ function_name }} POST request ')
result = await service.{{ function_name }}(data_b)

return result.dict()
logger.info(f'fast_api.j2 result is {result}')
try:
return result
except Exception as e:
logger.error(f'fast_api.j2 error while recognize {e}')
23 changes: 23 additions & 0 deletions piper/base/docker/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,29 @@ def render(self):
return template.render(cmd=self.cmd, python_docker_version=self.python_docker_version, run_command_lines=self.run_rows, post_install_lines=self.post_install_lines)


class TensorFlowImage:

def __init__(self, tag, python_docker_version, cmd, template_file, run_rows, post_install_lines):
self.tag = tag
self.python_docker_version = python_docker_version
self.cmd = cmd
self.template_file = template_file
self.run_rows = run_rows
self.post_install_lines = post_install_lines

def render(self):
"""
Render docker template
"""
template_dir = os.path.join(os.path.dirname(__file__), 'images')
jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir),
trim_blocks=True,
lstrip_blocks=True)
template = jinja_env.get_template(self.template_file)
return template.render(cmd=self.cmd, ddocker_name=self.tag, ddocker_version=self.python_docker_version, run_command_lines=self.run_rows, post_install_lines=self.post_install_lines)



# class PythonTesseractImage:

# def __init__(self, tag, python_docker_version, cmd):
Expand Down
15 changes: 15 additions & 0 deletions piper/base/docker/images/default-general.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM {{ddocker_name}}:{{ ddocker_version }}

{{ run_command_lines }}

WORKDIR /app

COPY requirements.txt ./requirements.txt
RUN PYTHONPATH=/usr/bin/python3 pip3 install -r requirements.txt

{{ post_install_lines }}

COPY ./ ./
RUN chmod +x ./run.sh

ENTRYPOINT ["{{ cmd }}"]
128 changes: 116 additions & 12 deletions piper/base/executors.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
import aiohttp
from loguru import logger
import docker
from docker.errors import APIError, BuildError
from pydantic import BaseModel #, BytesObject, ListOfStringsObject

from piper.base.docker import PythonImage
from piper.base.docker import PythonImage, TensorFlowImage
# from piper.base.docker import PythonTesseractImage
from piper.base.backend.utils import render_fast_api_backend, render_fast_api_tsrct_backend
from piper.envs import is_docker_env, is_current_env, get_env
Expand All @@ -29,6 +30,7 @@ class LocalExecutor:


def is_known(obj):
logger.info(f'test object {obj}')
basic = obj.__class__.__name__ in {'dict', 'list', 'tuple', 'str', 'int', 'float', 'bool'}
models = isinstance(obj, (BaseModel,))
return basic or models
Expand Down Expand Up @@ -109,10 +111,20 @@ def build_image(path: str, docker_image):
with open(f"{path}/Dockerfile", "w") as output:
output.write(image)

image, logs = client.images.build(path=path,
tag=docker_image.tag,
quiet=False,
timeout=20)
logger.info('build start!')

try:
image, logs = client.images.build(path=path,
tag=docker_image.tag,
quiet=False,
timeout=120)
except (BuildError, APIError) as e:
logger.error('error while build_image:')
for line in e.build_log:
if 'stream' in line:
logger.error(line['stream'].strip())
sys.exit()

for log in logs:
logger.info(f'executor build_image: {log}')
logger.info(f'image is {image}')
Expand Down Expand Up @@ -145,10 +157,11 @@ def wait_for_fast_api_app_start(host, external_port, wait_on_iter, n_iters):
if r.status_code == 200:
break
except Exception as e:
logger.error(f'resive health_check answer {e}')
time.sleep(wait_on_iter)

if i == n_iters:
logger.error('FastAPI app can`t start or n_iters too small')
logger.error(f'FastAPI app can`t start or wait_on_iter: {wait_on_iter} or n_iters: {n_iters} too small')
sys.exit()
i += 1

Expand Down Expand Up @@ -267,6 +280,99 @@ def __init__(self, port: int = 8080, **service_kwargs):

super().__init__('localhost', port, self.base_handler)


class FastAPIFaceRecognExecutor(HTTPExecutor):
# basic requements
requirements = ["gunicorn", "fastapi", "uvicorn", "aiohttp", "docker", "Jinja2", "pydantic", "loguru", "numpy", "opencv-python", "python-multipart", ]

# executor specific requements
requirements.extend(
[
# 'python3-opencv'
'tensorflow',
'mtcnn',
]
)

# basic packages
packages_list = ['apt-utils', 'tree', 'cmake', 'mc']

# executor specific packages
packages_list.extend(
[
'libgl1',
'ffmpeg',
'libsm6',
'libxext6',
]
)

base_handler = "recognize"

def __init__(self, port: int = 8080, **service_kwargs):
self.container = None
# self.image_tag = 'piper:latest'\
self.image_tag = 'tensorflow/tensorflow'
self.container_name = "piper_FastAPI_FaceRecogn"

if is_docker_env():
docker_client = docker.DockerClient(base_url='unix://var/run/docker.sock')
cfg = get_configuration()
project_output_path = cfg.path

copy_piper(project_output_path)
copy_scripts(project_output_path, self.scripts())

run_rows = ''
run_rows += add_row('RUN apt update && apt install -y apt-transport-https')
run_rows += add_row('RUN apt install -y software-properties-common')
run_rows += add_packages_to_install(self.packages_list)
run_rows += add_row('RUN pip3 install --upgrade pip')

post_install_lines = ""

docker_image = TensorFlowImage(self.image_tag, 'latest-gpu-jupyter', cmd=f"./run.sh", template_file='default-general.j2', run_rows=run_rows, post_install_lines=post_install_lines)
logger.info('Docker file created')

write_requirements(project_output_path, self.requirements)
logger.info('python requirements file created')

logger.info('build_image')
build_image(project_output_path, docker_image)

logger.info('image builded')
self.create_fast_api_files(project_output_path, **service_kwargs)

# create and run docker container
# if container exits it will be recreated!
logger.info('create image and container started')
container = du.create_image_and_container_by_dockerfile(
docker_client,
project_output_path,
self.image_tag,
self.container_name,
port
)

logger.info('waiting for FastApi service start')
if container:
output = container.attach(stdout=True, stderr=True, stream=False, logs=True)
for line in output:
logger.info(str(line))
#TODO test FastAPI errors by other way
if 'Traceback' in str(line):
logger.error('FastAPI can`t start')
sys.exit()
# logger.info(container.stats(decode=False, stream=False))

wait_for_fast_api_app_start('localhost', cfg.docker_app_port, cfg.wait_on_iter, cfg.n_iters)
else:
# TODO: Local ENVIRONMENT checks
pass

super().__init__('localhost', port, self.base_handler)


def rm_container(self):
if self.container:
self.container.remove(force=True)
Expand All @@ -278,21 +384,19 @@ def create_fast_api_files(self, path: str, **service_kwargs):
cfg = get_configuration()

# TODO add support more than one functions
backend = render_fast_api_tsrct_backend(
backend = render_fast_api_backend(
service_class=self.__class__.__name__,
service_kwargs=dict(service_kwargs),
scripts=self.scripts(),
function_name=self.base_handler,
# request_model="BytesObject",
# response_model="ListOfStringsObject"
request_model="BytesObject",
response_model="ListOfStringsObject"
)

with open(f"{path}/main.py", "w") as output:
output.write(backend)

write_requirements(path, self.requirements)

gunicorn = "#!/bin/bash \n" \
f"gunicorn -b 0.0.0.0:8080 --workers {cfg.n_gunicorn_workers} main:app --worker-class uvicorn.workers.UvicornWorker --preload --timeout 120"
f"gunicorn -b 0.0.0.0:8080 --workers {cfg.n_gunicorn_workers} main:app --worker-class uvicorn.workers.UvicornWorker --preload --timeout 240"
with open(f"{path}/run.sh", "w") as output:
output.write(gunicorn)
8 changes: 4 additions & 4 deletions piper/configurations.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ class Configuration:
env = None

# start time and counter
wait_on_iter = 0.5
n_iters = 10
wait_on_iter = 5
n_iters = 20

# docker start time and counter
docker_wait_on_iter = 0.5
docker_wait_on_iter = 4
docker_n_iters = 20

n_gunicorn_workers = 1
n_gunicorn_workers = 4

image_suffixes = set(['jpg', 'jpeg', 'png'])
pdf_suffixes = set(['pdf'])
Expand Down
12 changes: 7 additions & 5 deletions piper/envs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import imp
from piper.configurations import get_configuration
from loguru import logger

cfg = get_configuration()

Expand All @@ -18,7 +20,7 @@ def get_env():


def set_env(env):
print("Setting environment to: {}".format(env))
logger.info("Setting environment to: {}".format(env))
cfg.env = env


Expand All @@ -28,12 +30,12 @@ def __init__(self):
pass

def __enter__(self):
print("Entering DockerEnv")
logger.info("Docker context management __enter__")
self._old_environment = get_env()
set_env(self)

def __exit__(self, *args, **kws):
print("Exiting DockerEnv")
logger.info("Docker context management __exit__")
set_env(self._old_environment)


Expand All @@ -43,12 +45,12 @@ def __init__(self):
pass

def __enter__(self):
print("Entering CurrentEnv")
logger.info("CurrentEnv context management __enter__")
self._old_environment = get_env()
set_env(self)

def __exit__(self, *args, **kws):
print("Exiting CurrentEnv")
logger.info("CurrentEnv context management __exit__")
set_env(self._old_environment)


Expand Down
Loading