Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 3 additions & 1 deletion main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from piper.services import TestMessageAdder, StringValue
from envs import CurrentEnv
from piper.envs import CurrentEnv

import asyncio
from loguru import logger
logger.add("file.log", level="INFO")

if __name__ == '__main__':
loop = asyncio.get_event_loop()
Expand Down
36 changes: 22 additions & 14 deletions piper/base/executors.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from piper.base.backend.utils import render_fast_api_backend
from piper.envs import is_docker_env, is_current_env, get_env
from piper.configurations import get_configuration
from piper.utils import docker_utils as du


class BaseExecutor:
Expand Down Expand Up @@ -113,26 +114,33 @@ def run_container(image: str, ports: Dict[int, int]):


class FastAPIExecutor(HTTPExecutor):
requirements = ["gunicorn", "fastapi", "uvicorn", "aiohttp", "docker", "Jinja2", "pydantic"]
requirements = ["gunicorn", "fastapi", "uvicorn", "aiohttp", "docker", "Jinja2", "pydantic", "loguru"]
base_handler = "run"

def __init__(self, port: int = 8080, **service_kwargs):
self.container = None
self.image_tag = 'piper:latest'
self.container_name = "piper_FastAPI"

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

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

self.create_fast_api_files(path, **service_kwargs)

image_tag = self.__class__.__name__.lower()
docker_image = PythonImage(tag=image_tag,
python_docker_version="3.9",
cmd=f"./run.sh")
build_image(path, docker_image)
self.container = run_container(image_tag, {8080: port})
project_output_path = cfg.path

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

self.create_fast_api_files(project_output_path, **service_kwargs)

# create and run docker container
# if container exits it will be recreated!
du.create_image_and_container_by_dockerfile(
docker_client,
project_output_path,
self.image_tag,
self.container_name,
port
)
else:
# TODO: Local ENVIRONMENT checks
pass
Expand Down
1 change: 1 addition & 0 deletions piper/configurations.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
class Configuration:
path = "/Users/olegsokolov/PycharmProjects/piper/applications"
path = "/home/pavel/repo/piper_new_out/"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А можешь просто динамически находить как-то папку
типо
piper_path = os.getcwd()
applications_path = f"{piper_path}/applications
или вообще их может в /tmp/ сохранять, хотя лучше пока явно где-то рядом с основным

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

я бы вообще это в setting.py вынес, потому что юзеру решать где он хочет свой проект развернуть. А если свойство не указано, тогда на уровень выше и как текущая папка + '_out'

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А в чем отличие setting.py от configurations.py будет, более глобальные настройки?

piper_path = "piper"
default_env = "docker"
env = None
Expand Down
Empty file added piper/utils/__init__.py
Empty file.
144 changes: 144 additions & 0 deletions piper/utils/docker_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import docker
import time
import sys
from loguru import logger
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Прикольная вещь


def get_image(docker_client, image_name):
try:
cur_image = docker_client.images.get(image_name)
return cur_image
except docker.errors.ImageNotFound:
logger.info(f'image with tag {image_name} not found')
return False


def delete_image(docker_client, image_tag):
try:
result = docker_client.images.remove(image_tag, force=True)
return True
except Exception as e:
logger.error('error while remove image', e)
return False


def get_container(docker_client, container_name):
try:
cur_container = docker_client.containers.get(container_name)
return cur_container
except docker.errors.NotFound:
logger.info(f'container with name {container_name} not found')
return None
except Exception as e:
logger.error(f'non defined exeption {e}')
return None


def stop_container(docker_client, container_name):
try:
cur_container = docker_client.containers.get(container_name)
return cur_container.stop()
except docker.errors.NotFound:
logger.error(f'container for stop with name {container_name} not found')
except docker.errors.APIError:
logger.error(f'error while stop container {container_name}')


def remove_container(docker_client, container_name):
try:
cur_container = docker_client.containers.get(container_name)
return cur_container.remove(v=True, force=True)
except docker.errors.NotFound:
logger.error(f'container for stop with name {container_name} not found')
except docker.errors.APIError as de:
logger.error(f'error while remove container {container_name}')
logger.error(de)
return None


def stop_and_rm_container(docker_client, container_name):
# get container
cur_container = get_container(docker_client, container_name)

# get container status
if cur_container is None:
logger.info(f'container {container_name} not found')
return 'deleted'
else:
status = cur_container.status
cont_id = cur_container.id
logger.info('status', status, type(status))

if status == 'running':
logger.info(f'container {container_name} started already. Stop it!')
# stop
stop_result = stop_container(docker_client, cont_id)
logger.info('stoped', stop_result)
status = 'exited'
time.sleep(3)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Зачем тут sleep? вообще их нехорошо конечно нам везде вставлять, лучше опираться на сигналы от докера различные

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

да, тут лишние, тут можно опросить докер
по поводу sleep в container.run то средствами докера это не решить, докер говорит что статус изменен
с created на running, но внутри приложение еще не запустилось. мне кажется логично в fast api добавить
health_check и когда получим ответ, продолжать.


if status == 'exited':
logger.info(f'container {container_name} exists already. Remove it!')
# rm
remove_result = remove_container(docker_client, cont_id)
logger.info('removed, remove_result is ', remove_result)
time.sleep(3)
status = 'deleted'
return status


def image_find_and_rm(docker_client, image_tag):
cur_img = get_image(docker_client, image_tag)
logger.info(cur_img)
if cur_img:
logger.info(f'image {image_tag} exists')
logger.info(f'cur_img is {cur_img}, ID is {cur_img.id}')
del_result = delete_image(docker_client, image_tag)
logger.info(f'del_result of image {del_result}')
return del_result
else:
# не нужно ничего удалять, контейнера нет
return True


def create_image_and_container_by_dockerfile(docker_client, path, image_tag, container_name, port):
# should be deleted
status = stop_and_rm_container(docker_client, container_name)

cur_cont = get_container(docker_client, container_name)
if cur_cont is not None:
logger.error('container not deleted')
sys.exit()

# remove image
if status == 'deleted':
# remove image
del_result = image_find_and_rm(docker_client, image_tag)
if del_result:
# create new image
image, logs = docker_client.images.build(
path=path,
tag=image_tag,
quiet=False,
rm=True, # creates image only without container
forcerm=True,
timeout=20
)
for log in logs:
logger.debug(log)
logger.info('image', image)

# run container
try:
container = docker_client.containers.run(image_tag, name=container_name, detach=True, ports={8080:port})
for log in container.logs():
logger.debug(log)
logger.info('container', container)
time.sleep(10)
except docker.errors.APIError as api_e:
logger.error(f'eroror while run container {container_name}')
logger.error(str(api_e))
sys.exit()
else:
logger.error(f'error while del container')
sys.exit()

3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
docker
aiohttp
Jinja2
pydantic
pydantic
loguru