diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7a854a9 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +build: + python3 ./scripts/devtools/dev.py builder docker-compose build +destroy: + python3 ./scripts/devtools/dev.py builder docker-compose destroy +up: + python3 ./scripts/devtools/dev.py runner docker-compose up +down: + python3 ./scripts/devtools/dev.py runner docker-compose down +restart: + python3 ./scripts/devtools/dev.py runner docker-compose restart +setup: build up + +.PHONY: exec setup build diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..9ea0244 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,59 @@ +# ? docker-compose.yml for development environment + +# ! To start development you need to create a directory ./dummy_platform. +# ? Place files from the test platform into it: +# ? VM6: +# ? /opt/ispsystem/vm/config.json - configuration file +# ? /opt/ispsystem/vm/mysql - database directory +# ? DCI6: +# ? /opt/ispsystem/dci/config.json - configuration file +# ? /opt/ispsystem/dci/mysql - database directory + +# ? Create ./.env file and fill it with required vars: +# ? PLATFORM_TYPE='vm' +# ? Database container: +# ? MYSQL_DATABASE="database name" +# ? MYSQL_ROOT_PASSWORD="super secret password from config.json" + +# ? Launch: +# ? docker-compose up -d --force-recreate +# ? docker attach mgrctl + +services: + mgrctl: + container_name: mgrctl + restart: unless-stopped + build: + context: . + args: + - APP_VERSION=${APP_VERSION} + - APP_DIR=${APP_DIR} + - SRC_DIR=${SRC_DIR} + - PKG_NAME=${PKG_NAME} + - PKG_VERSION=${PKG_VERSION} + networks: + vm_box_net: null + volumes: + - type: bind + source: ./dummy_platform/opt/ispsystem/${PLATFORM_TYPE}/config.json + target: /opt/ispsystem/${PLATFORM_TYPE}/config.json + env_file: + - ./.env + tty: true + stdin_open: true + mysql: + container_name: mysql + image: docker-registry.ispsystem.com/mysql:5 + volumes: + - ./dummy_platform/opt/ispsystem/${PLATFORM_TYPE}/mysql:/var/lib/mysql + env_file: + - ./.env + labels: + autoconf_mysql: "true" + networks: + vm_box_net: null + command: --group-concat-max-len=131072 --max-connections=1000 --optimizer-search-depth=0 + +networks: + vm_box_net: + driver: bridge diff --git a/scripts/devtools/cli/__init__.py b/scripts/devtools/cli/__init__.py new file mode 100644 index 0000000..aff160b --- /dev/null +++ b/scripts/devtools/cli/__init__.py @@ -0,0 +1,10 @@ +# █▀▄▀█ █▀▀ ▀█▀ ▄▀█ ▀ +# █░▀░█ ██▄ ░█░ █▀█ ▄ +# -- -- -- -- -- -- - +__author__ = "MOIS3Y" +__credits__ = ["Stepan Zhukovsky"] +__license__ = "MIT" +__version__ = "0.1.0" +__maintainer__ = "Stepan Zhukovsky" +__email__ = "stepan@zhukovsky.me" +__status__ = "Development" diff --git a/scripts/devtools/cli/builder.py b/scripts/devtools/cli/builder.py new file mode 100644 index 0000000..acd7e62 --- /dev/null +++ b/scripts/devtools/cli/builder.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import click +import sh + +from .lazy_group import LazyGroup +from .utils import abort_if_false +from . import settings + + +@click.group() +def cli1(): + pass + + +@click.group( + cls=LazyGroup, + lazy_subcommands={'docker-compose': 'cli.builder.docker_compose'}, +) +def cli2(): + pass + + +@click.group(help='cmd for build/destroy project in docker containers') +def docker_compose(): + pass + + +@docker_compose.command(help='cmd for build project in docker containers') +def build(): + with sh.contrib.sudo(password=settings.SUDO_PASSWORD, _with=True): + sh.docker_compose('-f', settings.COMPOSE_FILE, 'build', _fg=True) + + +@docker_compose.command(help='cmd for destroy project in docker containers') +@click.option( + '--yes', + is_flag=True, + callback=abort_if_false, + expose_value=False, + prompt='Are you sure you want to destroy docker containers') +def destroy(): + with sh.contrib.sudo(password=settings.SUDO_PASSWORD, _with=True): + sh.docker_compose('-f', settings.COMPOSE_FILE, 'down', _fg=True) + + +cli = click.CommandCollection( + sources=[cli1, cli2], + help='cmd for building the project' +) diff --git a/scripts/devtools/cli/lazy_group.py b/scripts/devtools/cli/lazy_group.py new file mode 100644 index 0000000..fce9085 --- /dev/null +++ b/scripts/devtools/cli/lazy_group.py @@ -0,0 +1,39 @@ +import importlib +import click + + +class LazyGroup(click.Group): + def __init__(self, *args, lazy_subcommands=None, **kwargs): + super().__init__(*args, **kwargs) + # lazy_subcommands is a map of the form: + # + # {command-name} -> {module-name}.{command-object-name} + # + self.lazy_subcommands = lazy_subcommands or {} + + def list_commands(self, ctx): + base = super().list_commands(ctx) + lazy = sorted(self.lazy_subcommands.keys()) + return base + lazy + + def get_command(self, ctx, cmd_name): + if cmd_name in self.lazy_subcommands: + return self._lazy_load(cmd_name) + return super().get_command(ctx, cmd_name) + + def _lazy_load(self, cmd_name): + # lazily loading a command, + # first get the module name and attribute name + import_path = self.lazy_subcommands[cmd_name] + modname, cmd_object_name = import_path.rsplit(".", 1) + # do the import + mod = importlib.import_module(modname) + # get the Command object from that module + cmd_object = getattr(mod, cmd_object_name) + # check the result to make debugging easier + if not isinstance(cmd_object, click.BaseCommand): + raise ValueError( + f"Lazy loading of {import_path} failed by returning " + "a non-command object" + ) + return cmd_object diff --git a/scripts/devtools/cli/runner.py b/scripts/devtools/cli/runner.py new file mode 100644 index 0000000..804a295 --- /dev/null +++ b/scripts/devtools/cli/runner.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import click +import sh + +from .lazy_group import LazyGroup +from . import settings + + +@click.group() +def cli1(): + pass + + +@click.group( + cls=LazyGroup, + lazy_subcommands={'docker-compose': 'cli.runner.docker_compose'}, +) +def cli2(): + pass + + +@click.group(help='cmd for run/stop/restart project in docker containers') +def docker_compose(): + pass + + +@docker_compose.command(help='cmd for run project docker containers') +def up(): + with sh.contrib.sudo(password=settings.SUDO_PASSWORD, _with=True): + sh.docker_compose('-f', settings.COMPOSE_FILE, 'up', '-d', _fg=True) + + +@docker_compose.command(help='cmd for down project docker containers') +def down(): + with sh.contrib.sudo(password=settings.SUDO_PASSWORD, _with=True): + sh.docker_compose('-f', settings.COMPOSE_FILE, 'down', _fg=True) + + +@docker_compose.command(help='cmd for restart project docker containers') +def restart(): + with sh.contrib.sudo(password=settings.SUDO_PASSWORD, _with=True): + sh.docker_compose( + '-f', + settings.COMPOSE_FILE, + 'up', + '-d', + '--build', + '--force-recreate', + _fg=True + ) + + +cli = click.CommandCollection( + sources=[cli1, cli2], + help='cmd for running the project standalone or docker-compose' +) diff --git a/scripts/devtools/cli/settings.py b/scripts/devtools/cli/settings.py new file mode 100644 index 0000000..2f43b7e --- /dev/null +++ b/scripts/devtools/cli/settings.py @@ -0,0 +1,18 @@ +import pathlib +from environs import Env + + +# Path: +ROOT_DIR = pathlib.Path(__file__).resolve().parent.parent.parent.parent +PROJECT_DIR = ROOT_DIR / 'mgrctl' +COMPOSE_FILE = ROOT_DIR / 'docker-compose.yml' + +# Init environment: +env = Env() + +# read .env file, if it exists +# reed more about .env file here: https://github.com/sloria/environs +env.read_env(path=ROOT_DIR / '.env') + +# ! insecure save sudo password wherever +SUDO_PASSWORD = env.str('SUDO_PASSWORD', None) diff --git a/scripts/devtools/cli/utils.py b/scripts/devtools/cli/utils.py new file mode 100644 index 0000000..82c4806 --- /dev/null +++ b/scripts/devtools/cli/utils.py @@ -0,0 +1,15 @@ +import click + +from . import __version__ + + +def print_version(ctx, param, value): + if not value or ctx.resilient_parsing: + return + click.echo(__version__) + ctx.exit() + + +def abort_if_false(ctx, param, value): + if not value: + ctx.abort() diff --git a/scripts/devtools/dev.py b/scripts/devtools/dev.py new file mode 100755 index 0000000..ff6040e --- /dev/null +++ b/scripts/devtools/dev.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +import click + +from cli.lazy_group import LazyGroup +from cli.utils import print_version + + +@click.group( + cls=LazyGroup, + lazy_subcommands={ + 'builder': 'cli.builder.cli', + 'runner': 'cli.runner.cli', + }, + help='dev.py CLI tool for dev actions', +) +@click.option( + '--version', + is_flag=True, + callback=print_version, + expose_value=False, + is_eager=True, + help='Print version and exit' +) +def cli(): + pass + + +if __name__ == '__main__': + cli()