Add a super simple `marketstore` container supervisor

l1_precision_fix
Tyler Goodlet 2022-02-15 22:07:50 -05:00
parent 1cdb94374c
commit fbd3d1e308
1 changed files with 131 additions and 59 deletions

View File

@ -18,25 +18,33 @@
Supervisor for docker with included specific-image service helpers. Supervisor for docker with included specific-image service helpers.
''' '''
from typing import Optional from typing import (
from contextlib import contextmanager as cm Optional,
# Any,
)
from contextlib import asynccontextmanager as acm
# import time # import time
import trio import trio
import tractor import tractor
import docker import docker
import json import json
# from docker.containers import Container from docker.models.containers import Container
from requests import ConnectionError
from ..log import get_logger, get_console_log from ..log import get_logger # , get_console_log
from ..config import _config_dir
log = get_logger(__name__) log = get_logger(__name__)
_config = ''' _config = '''
# piker's ``marketstore`` config.
# mount this config using: # mount this config using:
# sudo docker run --mount type=bind,source="$HOME/.config/piker/",target="/etc" -i -p 5993:5993 alpacamarkets/marketstore:latest # sudo docker run --mount \
# type=bind,source="$HOME/.config/piker/",target="/etc" -i -p \
# 5993:5993 alpacamarkets/marketstore:latest
root_directory: data root_directory: data
listen_port: 5993 listen_port: 5993
grpc_listen_port: 5995 grpc_listen_port: 5995
@ -68,18 +76,58 @@ triggers:
''' '''
@cm @acm
def open_docker( async def open_docker(
url: Optional[str] = None, url: Optional[str] = None,
**kwargs, **kwargs,
) -> docker.DockerClient: ) -> docker.DockerClient:
# yield docker.Client( client = docker.DockerClient(
# base_url=url, base_url=url,
# **kwargs **kwargs
# ) if url else ) if url else docker.from_env(**kwargs)
yield docker.from_env(**kwargs)
try:
yield client
finally:
# for c in client.containers.list():
# c.kill()
client.close()
# client.api._custom_adapter.close()
# async def waitfor(
# cntr: Container,
# attr_path: tuple[str],
# expect=None,
# timeout: float = 0.5,
# ) -> Any:
# '''
# Wait for a container's attr value to be set. If ``expect`` is
# provided wait for the value to be set to that value.
# This is an async version of the helper from our ``pytest-dockerctl``
# plugin.
# '''
# def get(val, path):
# for key in path:
# val = val[key]
# return val
# start = time.time()
# while time.time() - start < timeout:
# cntr.reload()
# val = get(cntr.attrs, attr_path)
# if expect is None and val:
# return val
# elif val == expect:
# return val
# else:
# raise TimeoutError("{} failed to be {}, value: \"{}\"".format(
# attr_path, expect if expect else 'not None', val))
@tractor.context @tractor.context
@ -88,26 +136,44 @@ async def open_marketstore_container(
**kwargs, **kwargs,
) -> None: ) -> None:
log = get_console_log('info', name=__name__) '''
# this cli should "just work" Start and supervise a marketstore instance with its config bind-mounted
# sudo docker run --mount in from the piker config directory on the system.
# type=bind,source="$HOME/.config/piker/",target="/etc" -i -p
# 5993:5993 alpacamarkets/marketstore:latest
client = docker.from_env(**kwargs)
# with open_docker() as client: The equivalent cli cmd to this code is:
ctnr = client.containers.run(
'alpacamarkets/marketstore:latest', sudo docker run --mount \
[ type=bind,source="$HOME/.config/piker/",target="/etc" -i -p \
'--mount', 5993:5993 alpacamarkets/marketstore:latest
'type=bind,source="$HOME/.config/piker/",target="/etc"',
'-i', '''
'-p 5993:5993', # log = get_console_log('info', name=__name__)
],
detach=True, # client = docker.from_env(**kwargs)
async with open_docker() as client:
# create a mount from user's local piker config dir into container
config_dir_mnt = docker.types.Mount(
target='/etc',
source=_config_dir,
type='bind',
) )
cntr: Container = client.containers.run(
'alpacamarkets/marketstore:latest',
# do we need this for cmds?
# '-i',
# '-p 5993:5993',
ports={'5993/tcp': 5993},
mounts=[config_dir_mnt],
detach=True,
# stop_signal='SIGINT',
# init=True,
# remove=True,
)
try:
started: bool = False started: bool = False
logs = ctnr.logs(stream=True) logs = cntr.logs(stream=True)
with trio.move_on_after(0.5): with trio.move_on_after(0.5):
for entry in logs: for entry in logs:
@ -117,7 +183,6 @@ async def open_marketstore_container(
except json.JSONDecodeError: except json.JSONDecodeError:
if 'Error' in entry: if 'Error' in entry:
raise RuntimeError(entry) raise RuntimeError(entry)
# await tractor.breakpoint()
msg = record['msg'] msg = record['msg']
if "launching tcp listener for all services..." in msg: if "launching tcp listener for all services..." in msg:
@ -126,26 +191,33 @@ async def open_marketstore_container(
await trio.sleep(0) await trio.sleep(0)
if not started and ctnr not in client.containers.list(): if not started and cntr not in client.containers.list():
raise RuntimeError( raise RuntimeError(
'Failed to start `marketstore` check logs output for deats' 'Failed to start `marketstore` check logs output for deats'
) )
await ctx.started() await ctx.started()
await tractor.breakpoint() await trio.sleep_forever()
finally:
cntr.stop()
async def main(): async def main():
async with tractor.open_nursery( async with tractor.open_nursery(
loglevel='info', loglevel='runtime',
) as tn: ) as tn:
portal = await tn.start_actor('ahab', enable_modules=[__name__]) async with (
(
async with portal.open_context( await tn.start_actor('ahab', enable_modules=[__name__])
).open_context(
open_marketstore_container open_marketstore_container
) as (ctx, first),
) as (first, ctx): ):
assert not first
await trio.sleep_forever() await trio.sleep_forever()
if __name__ == '__main__': if __name__ == '__main__':
trio.run(main) trio.run(main)