mirror of
https://github.com/yavook/kiwi-scp.git
synced 2024-11-25 05:53:00 +00:00
Rework: FlexCommand defaults into lower hierarchy (override protected _run variants by default)
This commit is contained in:
parent
0060bf8878
commit
93d0b56eb7
24 changed files with 414 additions and 348 deletions
|
@ -46,7 +46,8 @@ class Parser:
|
|||
def get_args(self):
|
||||
if self.__args is None:
|
||||
# parse args if needed
|
||||
self.__args = self.__parser.parse_args()
|
||||
self.__args, unknowns = self.__parser.parse_known_args()
|
||||
self.__args.unknowns = unknowns
|
||||
|
||||
return self.__args
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ import logging
|
|||
|
||||
# local
|
||||
from . import subcommands
|
||||
from .config import LoadedConfig
|
||||
from .parser import Parser
|
||||
|
||||
|
||||
|
@ -13,7 +12,6 @@ class Runner:
|
|||
class __Runner:
|
||||
"""Singleton type"""
|
||||
|
||||
__parser = None
|
||||
__commands = []
|
||||
|
||||
def __init__(self):
|
||||
|
@ -22,10 +20,11 @@ class Runner:
|
|||
cmd = getattr(subcommands, className)
|
||||
self.__commands.append(cmd())
|
||||
|
||||
def run(self, command=None):
|
||||
def run(self, command=None, args=None):
|
||||
"""run the desired subcommand"""
|
||||
|
||||
args = Parser().get_args()
|
||||
if args is None:
|
||||
args = Parser().get_args()
|
||||
|
||||
if command is None:
|
||||
command = args.command
|
||||
|
@ -36,7 +35,7 @@ class Runner:
|
|||
logging.debug(f"Running '{cmd}' with args: {args}")
|
||||
|
||||
try:
|
||||
result = cmd.run(self, LoadedConfig.get(), args)
|
||||
result = cmd.run(self, args)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# system
|
||||
import logging
|
||||
import os
|
||||
|
||||
# local
|
||||
from .utils.project import Projects
|
||||
|
@ -16,7 +17,9 @@ class SubCommand:
|
|||
# command parser
|
||||
_sub_parser = None
|
||||
|
||||
def __init__(self, name, add_parser=True, **kwargs):
|
||||
_action = None
|
||||
|
||||
def __init__(self, name, action='', add_parser=True, **kwargs):
|
||||
self.__name = name
|
||||
if add_parser:
|
||||
self._sub_parser = Parser().get_subparsers().add_parser(
|
||||
|
@ -24,26 +27,38 @@ class SubCommand:
|
|||
**kwargs
|
||||
)
|
||||
|
||||
if not action:
|
||||
# default action string
|
||||
self._action = f"Running '{str(self)}' for"
|
||||
else:
|
||||
self._action = action
|
||||
|
||||
def __str__(self):
|
||||
return self.__name
|
||||
|
||||
def run(self, runner, config, args):
|
||||
"""actually run command with this dir's config and parsed CLI args"""
|
||||
def _run_instance(self, runner, args):
|
||||
pass
|
||||
|
||||
def run(self, runner, args):
|
||||
"""actually run command with parsed CLI args"""
|
||||
|
||||
# run for entire instance
|
||||
logging.info(f"{self._action} kiwi-config instance at '{os.getcwd()}'")
|
||||
return self._run_instance(runner, args)
|
||||
|
||||
|
||||
class ProjectCommand(SubCommand):
|
||||
"""this command concerns a project in current instance"""
|
||||
|
||||
def __init__(self, name, num_projects, add_parser=True, **kwargs):
|
||||
def __init__(self, name, num_projects, action='', add_parser=True, **kwargs):
|
||||
super().__init__(
|
||||
name, add_parser=add_parser,
|
||||
name, action=action, add_parser=add_parser,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
projects = "a project"
|
||||
|
||||
if not num_projects == 1:
|
||||
if num_projects == 1:
|
||||
projects = "a project"
|
||||
else:
|
||||
projects = "project(s)"
|
||||
|
||||
self._sub_parser.add_argument(
|
||||
|
@ -51,19 +66,41 @@ class ProjectCommand(SubCommand):
|
|||
help=f"select {projects} in this instance"
|
||||
)
|
||||
|
||||
def _run_instance(self, runner, args):
|
||||
# default: run for all enabled projects
|
||||
return self._run_projects(runner, args, Projects.from_dir().filter_enabled())
|
||||
|
||||
def _run_projects(self, runner, args, projects):
|
||||
pass
|
||||
|
||||
def run(self, runner, args):
|
||||
projects = Projects.from_args(args)
|
||||
|
||||
if not projects.empty():
|
||||
# project(s) given
|
||||
logging.info(f"{self._action} projects {projects}")
|
||||
return self._run_projects(runner, args, projects)
|
||||
|
||||
else:
|
||||
return super().run(runner, args)
|
||||
|
||||
|
||||
class ServiceCommand(ProjectCommand):
|
||||
"""this command concerns service(s) in a project"""
|
||||
|
||||
def __init__(self, name, num_projects, num_services, add_parser=True, **kwargs):
|
||||
def __init__(self, name, num_projects, num_services, action='', add_parser=True, **kwargs):
|
||||
super().__init__(
|
||||
name, num_projects=num_projects, add_parser=add_parser,
|
||||
name, num_projects=num_projects, action=action, add_parser=add_parser,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
services = "a service"
|
||||
if (isinstance(num_projects, str) and num_projects == '*') \
|
||||
or (isinstance(num_projects, int) and num_projects > 1):
|
||||
logging.warning(f"Invalid choice for project count: {num_projects}")
|
||||
|
||||
if not num_services == 1:
|
||||
if num_services == 1:
|
||||
services = "a service"
|
||||
else:
|
||||
services = "service(s)"
|
||||
|
||||
self._sub_parser.add_argument(
|
||||
|
@ -71,52 +108,25 @@ class ServiceCommand(ProjectCommand):
|
|||
help=f"select {services} in a project"
|
||||
)
|
||||
|
||||
|
||||
class FlexCommand(ServiceCommand):
|
||||
"""this command concerns the entire instance, a whole project or just service(s) in a project"""
|
||||
|
||||
__action = None
|
||||
|
||||
def __init__(self, name, action='', add_parser=True, **kwargs):
|
||||
super().__init__(
|
||||
name, num_projects='?', num_services='*', add_parser=add_parser,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
if not action:
|
||||
# default action string
|
||||
self.__action = f"Running '{str(self)}' for"
|
||||
else:
|
||||
self.__action = action
|
||||
|
||||
def _run_instance(self, runner, config, args):
|
||||
def _run_projects(self, runner, args, projects):
|
||||
result = True
|
||||
|
||||
for project in Projects.from_args(args):
|
||||
args.projects = project.get_name()
|
||||
result &= runner.run(str(self))
|
||||
# default: run without services for all given
|
||||
for project in projects:
|
||||
result &= self._run_services(runner, args, project, [])
|
||||
|
||||
return result
|
||||
|
||||
def _run_project(self, runner, config, args):
|
||||
return self._run_services(runner, config, args, [])
|
||||
|
||||
def _run_services(self, runner, config, args, services):
|
||||
def _run_services(self, runner, args, project, services):
|
||||
pass
|
||||
|
||||
def run(self, runner, config, args):
|
||||
projects = Projects.from_args(args)
|
||||
if not projects:
|
||||
# no project given, run for entire instance
|
||||
logging.info(f"{self.__action} this instance")
|
||||
return self._run_instance(runner, config, args)
|
||||
def run(self, runner, args):
|
||||
if 'services' in args and args.services:
|
||||
project = Projects.from_args(args)[0]
|
||||
|
||||
project = projects[0]
|
||||
if args is None or 'services' not in args or not args.services:
|
||||
# no services given, run for whole project
|
||||
logging.info(f"{self.__action} project '{project.get_name()}'")
|
||||
return self._run_project(runner, config, args)
|
||||
# run for service(s) inside project
|
||||
logging.info(f"{self._action} project '{project.get_name()}', services {args.services}")
|
||||
return self._run_services(runner, args, project, args.services)
|
||||
|
||||
# run for service(s) inside project
|
||||
logging.info(f"{self.__action} services {args.services} in project '{project.get_name()}'")
|
||||
return self._run_services(runner, config, args, args.services)
|
||||
else:
|
||||
return super().run(runner, args)
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
# local
|
||||
from ._subcommand import FlexCommand
|
||||
from ._subcommand import ServiceCommand
|
||||
from .utils.dockercommand import DockerCommand
|
||||
|
||||
|
||||
class BuildCommand(FlexCommand):
|
||||
class BuildCommand(ServiceCommand):
|
||||
"""kiwi build"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
'build', "Building images for",
|
||||
'build', num_projects='?', num_services='*',
|
||||
action="Building images for",
|
||||
description="Build images for the whole instance, a project or service(s) inside a project"
|
||||
)
|
||||
|
||||
def _run_services(self, runner, config, args, services):
|
||||
DockerCommand('docker-compose').run(
|
||||
config, args, ['build', '--pull', *services]
|
||||
)
|
||||
def _run_services(self, runner, args, project, services):
|
||||
DockerCommand('docker-compose').run(project, [
|
||||
'build', '--pull', *services
|
||||
])
|
||||
return True
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
# system
|
||||
import logging
|
||||
|
||||
# local
|
||||
from ._subcommand import ProjectCommand
|
||||
from .utils.dockercommand import DockerCommand
|
||||
|
@ -9,19 +12,32 @@ class CmdCommand(ProjectCommand):
|
|||
def __init__(self):
|
||||
super().__init__(
|
||||
'cmd', num_projects=1,
|
||||
action="Running docker-compose in",
|
||||
description="Run raw docker-compose command in a project"
|
||||
)
|
||||
|
||||
# command string after docker-compose
|
||||
# command for docker-compose
|
||||
self._sub_parser.add_argument(
|
||||
'compose_cmd', metavar='cmd', type=str,
|
||||
help="runs `docker-compose <cmd>`"
|
||||
help="command for 'docker-compose'"
|
||||
)
|
||||
|
||||
def run(self, runner, config, args):
|
||||
import shlex
|
||||
# arguments for docker-compose command
|
||||
self._sub_parser.add_argument(
|
||||
'compose_args', metavar='arg', nargs='*', type=str,
|
||||
help="arguments for 'docker-compose' commands"
|
||||
)
|
||||
|
||||
def _run_projects(self, runner, args, projects):
|
||||
if args.unknowns:
|
||||
args.compose_args = [*args.compose_args, *args.unknowns]
|
||||
args.unknowns = []
|
||||
|
||||
logging.debug(f"Updated args: {args}")
|
||||
|
||||
# run with split compose_cmd argument
|
||||
DockerCommand('docker-compose').run(config, args, shlex.split(args.compose_cmd))
|
||||
DockerCommand('docker-compose').run(projects[0], [
|
||||
args.compose_cmd, *args.compose_args
|
||||
])
|
||||
|
||||
return True
|
||||
|
|
|
@ -10,6 +10,7 @@ from .utils.rootkit import Rootkit, prefix_path_mnt
|
|||
|
||||
# parent
|
||||
from .._constants import CONF_DIRECTORY_NAME
|
||||
from ..config import LoadedConfig
|
||||
|
||||
|
||||
class ConfCopyCommand(SubCommand):
|
||||
|
@ -21,22 +22,20 @@ class ConfCopyCommand(SubCommand):
|
|||
description="Synchronize all config files to target directory"
|
||||
)
|
||||
|
||||
def run(self, runner, config, args):
|
||||
def _run_instance(self, runner, args):
|
||||
conf_dirs = [
|
||||
project.conf_dir_name()
|
||||
for project in Projects.all()
|
||||
if project.is_enabled()
|
||||
for project in Projects.from_dir().filter_enabled()
|
||||
]
|
||||
|
||||
if conf_dirs:
|
||||
# add target directory
|
||||
conf_dirs.append(config['runtime:storage'])
|
||||
conf_dirs.append(LoadedConfig.get()['runtime:storage'])
|
||||
logging.info(f"Sync directories: {conf_dirs}")
|
||||
|
||||
Rootkit('rsync').run(
|
||||
config, args, ['rsync', '-r', *prefix_path_mnt(conf_dirs)],
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
|
||||
)
|
||||
Rootkit('rsync').run([
|
||||
'rsync', '-r', *prefix_path_mnt(conf_dirs)
|
||||
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -50,14 +49,13 @@ class ConfPurgeCommand(SubCommand):
|
|||
description="Remove all config files in target directory"
|
||||
)
|
||||
|
||||
def run(self, runner, config, args):
|
||||
conf_target = f"{config['runtime:storage']}/{CONF_DIRECTORY_NAME}"
|
||||
def _run_instance(self, runner, args):
|
||||
conf_target = f"{LoadedConfig.get()['runtime:storage']}/{CONF_DIRECTORY_NAME}"
|
||||
logging.info(f"Purging directories: {conf_target}")
|
||||
|
||||
Rootkit().run(
|
||||
config, args, ['rm', '-rf', prefix_path_mnt(conf_target)],
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
|
||||
)
|
||||
Rootkit().run([
|
||||
'rm', '-rf', prefix_path_mnt(conf_target)
|
||||
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
|
||||
return True
|
||||
|
||||
|
@ -71,12 +69,12 @@ class ConfCleanCommand(SubCommand):
|
|||
description="Cleanly sync all configs to target folder, relaunch affected projects"
|
||||
)
|
||||
|
||||
def run(self, runner, config, args):
|
||||
def _run_instance(self, runner, args):
|
||||
result = True
|
||||
|
||||
affected_projects = [
|
||||
project.conf_dir_name()
|
||||
for project in Projects.all()
|
||||
for project in Projects.from_dir()
|
||||
if project.has_configs()
|
||||
]
|
||||
|
||||
|
|
|
@ -9,11 +9,12 @@ class DisableCommand(ProjectCommand):
|
|||
def __init__(self):
|
||||
super().__init__(
|
||||
'disable', num_projects='+',
|
||||
action="Disabling",
|
||||
description="Disable whole project(s) in this instance"
|
||||
)
|
||||
|
||||
def run(self, runner, config, args):
|
||||
def _run_projects(self, runner, args, projects):
|
||||
return all([
|
||||
project.disable()
|
||||
for project in Projects.from_args(args)
|
||||
for project in projects
|
||||
])
|
||||
|
|
|
@ -1,40 +1,42 @@
|
|||
# local
|
||||
from ._subcommand import FlexCommand
|
||||
from ._subcommand import ServiceCommand
|
||||
from .utils.dockercommand import DockerCommand
|
||||
from .utils.misc import are_you_sure
|
||||
|
||||
|
||||
class DownCommand(FlexCommand):
|
||||
class DownCommand(ServiceCommand):
|
||||
"""kiwi down"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
'down', "Bringing down",
|
||||
'down', num_projects='?', num_services='*',
|
||||
action="Bringing down",
|
||||
description="Bring down the whole instance, a project or service(s) inside a project"
|
||||
)
|
||||
|
||||
def _run_instance(self, runner, config, args):
|
||||
def _run_instance(self, runner, args):
|
||||
if are_you_sure([
|
||||
"This will bring down the entire instance.",
|
||||
"",
|
||||
"This may not be what you intended, because:",
|
||||
" - Bringing down the instance stops ALL services in here",
|
||||
]):
|
||||
return super()._run_instance(runner, config, args)
|
||||
return super()._run_instance(runner, args)
|
||||
|
||||
return False
|
||||
|
||||
def _run_project(self, runner, config, args):
|
||||
DockerCommand('docker-compose').run(
|
||||
config, args, ['down']
|
||||
)
|
||||
def _run_projects(self, runner, args, projects):
|
||||
for project in projects:
|
||||
DockerCommand('docker-compose').run(project, [
|
||||
'down'
|
||||
])
|
||||
return True
|
||||
|
||||
def _run_services(self, runner, config, args, services):
|
||||
DockerCommand('docker-compose').run(
|
||||
config, args, ['stop', *services]
|
||||
)
|
||||
DockerCommand('docker-compose').run(
|
||||
config, args, ['rm', '-f', *services]
|
||||
)
|
||||
def _run_services(self, runner, args, project, services):
|
||||
DockerCommand('docker-compose').run(project, [
|
||||
'stop', *services
|
||||
])
|
||||
DockerCommand('docker-compose').run(project, [
|
||||
'rm', '-f', *services
|
||||
])
|
||||
return True
|
||||
|
|
|
@ -9,11 +9,12 @@ class EnableCommand(ProjectCommand):
|
|||
def __init__(self):
|
||||
super().__init__(
|
||||
'enable', num_projects='+',
|
||||
action="Enabling",
|
||||
description="Enable whole project(s) in this instance"
|
||||
)
|
||||
|
||||
def run(self, runner, config, args):
|
||||
def _run_projects(self, runner, args, projects):
|
||||
return all([
|
||||
project.enable()
|
||||
for project in Projects.from_args(args)
|
||||
for project in projects
|
||||
])
|
||||
|
|
|
@ -7,6 +7,7 @@ from ._subcommand import SubCommand
|
|||
|
||||
# parent (display purposes only)
|
||||
from .._constants import KIWI_CONF_NAME
|
||||
from ..config import DefaultConfig, LoadedConfig
|
||||
|
||||
|
||||
def user_input(config, key, prompt):
|
||||
|
@ -40,12 +41,12 @@ class InitCommand(SubCommand):
|
|||
help=f"use default values even if {KIWI_CONF_NAME} is present"
|
||||
)
|
||||
|
||||
def run(self, runner, config, args):
|
||||
def _run_instance(self, runner, args):
|
||||
logging.info(f"Initializing '{KIWI_CONF_NAME}' in '{os.getcwd()}'")
|
||||
config = LoadedConfig.get()
|
||||
|
||||
# check force switch
|
||||
if args.force and os.path.isfile(KIWI_CONF_NAME):
|
||||
from ..config import DefaultConfig
|
||||
|
||||
logging.warning(f"Overwriting existing '{KIWI_CONF_NAME}'!")
|
||||
config = DefaultConfig.get()
|
||||
|
|
|
@ -1,84 +1,75 @@
|
|||
# system
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import yaml
|
||||
|
||||
# local
|
||||
from ._subcommand import FlexCommand
|
||||
from .utils.dockercommand import DockerCommand
|
||||
from .utils.project import Projects
|
||||
from ._subcommand import ServiceCommand
|
||||
from .utils.project import Project, Projects
|
||||
|
||||
|
||||
def _print_list(strings):
|
||||
if isinstance(strings, list):
|
||||
if isinstance(strings, str):
|
||||
print(f" - {strings}")
|
||||
|
||||
elif isinstance(strings, Project):
|
||||
_print_list(strings.get_name())
|
||||
|
||||
elif isinstance(strings, list):
|
||||
for string in strings:
|
||||
print(f" - {string}")
|
||||
_print_list(string)
|
||||
|
||||
elif isinstance(strings, str):
|
||||
_print_list(strings.strip().split('\n'))
|
||||
|
||||
elif isinstance(strings, bytes):
|
||||
_print_list(str(strings, 'utf-8'))
|
||||
else:
|
||||
_print_list(list(strings))
|
||||
|
||||
|
||||
class ListCommand(FlexCommand):
|
||||
class ListCommand(ServiceCommand):
|
||||
"""kiwi list"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
'list', "Listing",
|
||||
'list', num_projects='?', num_services='*',
|
||||
action="Listing",
|
||||
description="List projects in this instance, services inside a project or service(s) inside a project"
|
||||
)
|
||||
|
||||
def _run_instance(self, runner, config, args):
|
||||
def _run_instance(self, runner, args):
|
||||
print(f"kiwi-config instance at '{os.getcwd()}'")
|
||||
print("#########")
|
||||
projects = Projects.all()
|
||||
projects = Projects.from_dir()
|
||||
|
||||
enableds = [
|
||||
project.get_name()
|
||||
for project in projects
|
||||
if project.is_enabled()
|
||||
]
|
||||
|
||||
if enableds:
|
||||
enabled_projects = projects.filter_enabled()
|
||||
if not enabled_projects.empty():
|
||||
print(f"Enabled projects:")
|
||||
_print_list(enableds)
|
||||
_print_list(enabled_projects)
|
||||
|
||||
disableds = [
|
||||
project.get_name()
|
||||
for project in projects
|
||||
if project.is_disabled()
|
||||
]
|
||||
|
||||
if disableds:
|
||||
disabled_projects = projects.filter_disabled()
|
||||
if not disabled_projects.empty():
|
||||
print(f"Disabled projects:")
|
||||
_print_list(disableds)
|
||||
_print_list(disabled_projects)
|
||||
|
||||
return True
|
||||
|
||||
def _run_project(self, runner, config, args):
|
||||
project = Projects.from_args(args)[0]
|
||||
|
||||
def _run_projects(self, runner, args, projects):
|
||||
project = projects[0]
|
||||
if not project.exists():
|
||||
logging.error(f"Project '{project.get_name()}' not found")
|
||||
logging.warning(f"Project '{project.get_name()}' not found")
|
||||
return False
|
||||
|
||||
print(f"Services in project '{project.get_name()}':")
|
||||
print("#########")
|
||||
|
||||
ps = DockerCommand('docker-compose').run(
|
||||
config, args, ['config', '--services'],
|
||||
stdout=subprocess.PIPE
|
||||
)
|
||||
with open(project.compose_file_name(), 'r') as stream:
|
||||
try:
|
||||
docker_compose_yml = yaml.safe_load(stream)
|
||||
_print_list(docker_compose_yml['services'].keys())
|
||||
|
||||
except yaml.YAMLError as exc:
|
||||
logging.error(exc)
|
||||
|
||||
_print_list(ps.stdout)
|
||||
return True
|
||||
|
||||
def _run_services(self, runner, config, args, services):
|
||||
project = Projects.from_args(args)[0]
|
||||
|
||||
def _run_services(self, runner, args, project, services):
|
||||
if not project.exists():
|
||||
logging.error(f"Project '{project.get_name()}' not found")
|
||||
return False
|
||||
|
@ -91,10 +82,13 @@ class ListCommand(FlexCommand):
|
|||
docker_compose_yml = yaml.safe_load(stream)
|
||||
|
||||
for service_name in services:
|
||||
print(yaml.dump(
|
||||
{service_name: docker_compose_yml['services'][service_name]},
|
||||
default_flow_style=False, sort_keys=False
|
||||
).strip())
|
||||
try:
|
||||
print(yaml.dump(
|
||||
{service_name: docker_compose_yml['services'][service_name]},
|
||||
default_flow_style=False, sort_keys=False
|
||||
).strip())
|
||||
except KeyError:
|
||||
logging.error(f"Service '{service_name}' not found")
|
||||
|
||||
return True
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ class LogsCommand(ServiceCommand):
|
|||
def __init__(self):
|
||||
super().__init__(
|
||||
'logs', num_projects=1, num_services='*',
|
||||
action="Showing logs of",
|
||||
description="Show logs of a project or service(s) of a project"
|
||||
)
|
||||
|
||||
|
@ -18,7 +19,7 @@ class LogsCommand(ServiceCommand):
|
|||
help="output appended data as log grows"
|
||||
)
|
||||
|
||||
def run(self, runner, config, args):
|
||||
def _run_services(self, runner, args, project, services):
|
||||
# include timestamps
|
||||
compose_cmd = ['logs', '-t']
|
||||
|
||||
|
@ -27,13 +28,13 @@ class LogsCommand(ServiceCommand):
|
|||
compose_cmd = [*compose_cmd, '-f', '--tail=10']
|
||||
|
||||
# append if one or more services are given
|
||||
if args.services:
|
||||
if services:
|
||||
compose_cmd = [*compose_cmd, *args.services]
|
||||
|
||||
# use 'less' viewer if output will be static
|
||||
if args.follow:
|
||||
DockerCommand('docker-compose').run(config, args, compose_cmd)
|
||||
DockerCommand('docker-compose').run(project, compose_cmd)
|
||||
else:
|
||||
DockerCommand('docker-compose').run_less(config, args, compose_cmd)
|
||||
DockerCommand('docker-compose').run_less(project, compose_cmd)
|
||||
|
||||
return True
|
||||
|
|
|
@ -7,16 +7,18 @@ from ._subcommand import SubCommand
|
|||
from .utils.dockercommand import DockerCommand
|
||||
from .utils.misc import are_you_sure
|
||||
|
||||
# parent
|
||||
from ..config import LoadedConfig
|
||||
|
||||
def _find_net(config, args):
|
||||
ps = DockerCommand('docker').run(
|
||||
config, args, ['network', 'ls', '--filter', f"name={config['network:name']}", '--format', '{{.Name}}'],
|
||||
stdout=subprocess.PIPE
|
||||
)
|
||||
|
||||
def _find_net(net_name):
|
||||
ps = DockerCommand('docker').run(None, [
|
||||
'network', 'ls', '--filter', f"name={net_name}", '--format', '{{.Name}}'
|
||||
], stdout=subprocess.PIPE)
|
||||
|
||||
net_found = str(ps.stdout, 'utf-8').strip()
|
||||
|
||||
return net_found == config['network:name']
|
||||
return net_found == net_name
|
||||
|
||||
|
||||
class NetUpCommand(SubCommand):
|
||||
|
@ -25,30 +27,31 @@ class NetUpCommand(SubCommand):
|
|||
def __init__(self):
|
||||
super().__init__(
|
||||
'net-up',
|
||||
action="Creating the local network hub",
|
||||
description="Create the local network hub for this instance"
|
||||
)
|
||||
|
||||
def run(self, runner, config, args):
|
||||
if _find_net(config, args):
|
||||
logging.info(f"Network '{config['network:name']}' already exists")
|
||||
def _run_instance(self, runner, args):
|
||||
config = LoadedConfig.get()
|
||||
net_name = config['network:name']
|
||||
net_cidr = config['network:cidr']
|
||||
|
||||
if _find_net(net_name):
|
||||
logging.info(f"Network '{net_name}' already exists")
|
||||
return True
|
||||
|
||||
try:
|
||||
DockerCommand('docker').run(
|
||||
config, args,
|
||||
[
|
||||
'network', 'create',
|
||||
'--driver', 'bridge',
|
||||
'--internal',
|
||||
'--subnet', config['network:cidr'],
|
||||
config['network:name']
|
||||
],
|
||||
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
|
||||
)
|
||||
logging.info(f"Network '{config['network:name']}' created")
|
||||
DockerCommand('docker').run(None, [
|
||||
'network', 'create',
|
||||
'--driver', 'bridge',
|
||||
'--internal',
|
||||
'--subnet', net_cidr,
|
||||
net_name
|
||||
], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
logging.info(f"Network '{net_name}' created")
|
||||
|
||||
except subprocess.CalledProcessError:
|
||||
logging.error(f"Error creating network '{config['network:name']}'")
|
||||
logging.error(f"Error creating network '{net_name}'")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
@ -63,25 +66,26 @@ class NetDownCommand(SubCommand):
|
|||
description="Remove the local network hub for this instance"
|
||||
)
|
||||
|
||||
def run(self, runner, config, args):
|
||||
if not _find_net(config, args):
|
||||
logging.info(f"Network '{config['network:name']}' does not exist")
|
||||
def _run_instance(self, runner, args):
|
||||
net_name = LoadedConfig.get()['network:name']
|
||||
|
||||
if not _find_net(net_name):
|
||||
logging.info(f"Network '{net_name}' does not exist")
|
||||
return True
|
||||
|
||||
try:
|
||||
if are_you_sure("This will bring down this instance's hub network!"):
|
||||
if runner.run('down'):
|
||||
DockerCommand('docker').run(
|
||||
config, args,
|
||||
['network', 'rm', config['network:name']],
|
||||
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
|
||||
)
|
||||
logging.info(f"Network '{config['network:name']}' removed")
|
||||
DockerCommand('docker').run(None, [
|
||||
'network', 'rm', net_name
|
||||
], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
|
||||
logging.info(f"Network '{net_name}' removed")
|
||||
else:
|
||||
return False
|
||||
|
||||
except subprocess.CalledProcessError:
|
||||
logging.error(f"Error removing network '{config['network:name']}'")
|
||||
logging.error(f"Error removing network '{net_name}'")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
|
|
@ -17,12 +17,12 @@ class NewCommand(ProjectCommand):
|
|||
def __init__(self):
|
||||
super().__init__(
|
||||
'new', num_projects='+',
|
||||
action="Creating",
|
||||
description="Create new empty project(s) in this instance"
|
||||
)
|
||||
|
||||
def run(self, runner, config, args):
|
||||
def _run_projects(self, runner, args, projects):
|
||||
result = True
|
||||
projects = Projects.from_args(args)
|
||||
|
||||
for project in projects:
|
||||
if project.exists():
|
||||
|
@ -31,7 +31,7 @@ class NewCommand(ProjectCommand):
|
|||
|
||||
else:
|
||||
logging.info(f"Creating project '{project.get_name()}'")
|
||||
os.mkdir(project.enabled_dir_name())
|
||||
os.mkdir(project.disabled_dir_name())
|
||||
shutil.copy(DEFAULT_DOCKER_COMPOSE_NAME, project.compose_file_name())
|
||||
|
||||
return result
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
# local
|
||||
from ._subcommand import FlexCommand
|
||||
from ._subcommand import ServiceCommand
|
||||
from .utils.dockercommand import DockerCommand
|
||||
|
||||
|
||||
class PullCommand(FlexCommand):
|
||||
class PullCommand(ServiceCommand):
|
||||
"""kiwi pull"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
'pull', "Pulling images for",
|
||||
'pull', num_projects='?', num_services='*',
|
||||
action="Pulling images for",
|
||||
description="Pull images for the whole instance, a project or service(s) inside a project"
|
||||
)
|
||||
|
||||
def _run_services(self, runner, config, args, services):
|
||||
DockerCommand('docker-compose').run(
|
||||
config, args, ['pull', '--ignore-pull-failures', *services]
|
||||
)
|
||||
def _run_services(self, runner, args, project, services):
|
||||
DockerCommand('docker-compose').run(project, [
|
||||
'pull', '--ignore-pull-failures', *services
|
||||
])
|
||||
return True
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
# local
|
||||
from ._subcommand import FlexCommand
|
||||
from ._subcommand import ServiceCommand
|
||||
from .utils.dockercommand import DockerCommand
|
||||
|
||||
|
||||
class PushCommand(FlexCommand):
|
||||
class PushCommand(ServiceCommand):
|
||||
"""kiwi push"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
'push', "Pushing images for",
|
||||
'push', num_projects='?', num_services='*',
|
||||
action="Pushing images for",
|
||||
description="Push images for the whole instance, a project or service(s) inside a project"
|
||||
)
|
||||
|
||||
def _run_services(self, runner, config, args, services):
|
||||
DockerCommand('docker-compose').run(
|
||||
config, args, ['push', *services]
|
||||
)
|
||||
def _run_services(self, runner, args, project, services):
|
||||
DockerCommand('docker-compose').run(project, [
|
||||
'push', *services
|
||||
])
|
||||
return True
|
||||
|
|
|
@ -6,19 +6,21 @@ import subprocess
|
|||
from ._subcommand import ServiceCommand
|
||||
from .utils.dockercommand import DockerCommand
|
||||
|
||||
# parent
|
||||
from ..config import LoadedConfig
|
||||
|
||||
def _service_has_executable(config, args, compose_cmd, exe_name):
|
||||
|
||||
def _service_has_executable(project, service, exe_name):
|
||||
"""
|
||||
Test if container (as of compose_cmd array) has an executable exe_name in its PATH.
|
||||
Test if service in project has an executable exe_name in its PATH.
|
||||
Requires /bin/sh and which.
|
||||
"""
|
||||
|
||||
try:
|
||||
# test if desired shell exists
|
||||
DockerCommand('docker-compose').run(
|
||||
config, args, [*compose_cmd, '/bin/sh', '-c', f"which {exe_name}"],
|
||||
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
|
||||
)
|
||||
DockerCommand('docker-compose').run(project, [
|
||||
'exec', service, '/bin/sh', '-c', f"which {exe_name}"
|
||||
], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
return True
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
|
@ -26,13 +28,14 @@ def _service_has_executable(config, args, compose_cmd, exe_name):
|
|||
return False
|
||||
|
||||
|
||||
def _find_shell(config, args, compose_cmd):
|
||||
"""find first working shell (provided by config and args) in container (as of compose_cmd array)"""
|
||||
def _find_shell(args, project, service):
|
||||
"""find first working shell (provided by config and args) in service in project"""
|
||||
|
||||
# builtin shells: as a last resort, fallback to '/bin/sh' and 'sh'
|
||||
shells = ['/bin/sh', 'sh']
|
||||
|
||||
# load favorite shells from config
|
||||
config = LoadedConfig.get()
|
||||
if config['runtime:shells']:
|
||||
shells = [*config['runtime:shells'], *shells]
|
||||
|
||||
|
@ -44,7 +47,7 @@ def _find_shell(config, args, compose_cmd):
|
|||
|
||||
# actually try shells
|
||||
for i, shell in enumerate(shells):
|
||||
if _service_has_executable(config, args, compose_cmd, shell):
|
||||
if _service_has_executable(project, service, shell):
|
||||
# found working shell
|
||||
logging.debug(f"Using shell '{shell}'")
|
||||
return shell
|
||||
|
@ -82,15 +85,15 @@ class ShCommand(ServiceCommand):
|
|||
help="shell to spawn"
|
||||
)
|
||||
|
||||
def run(self, runner, config, args):
|
||||
compose_cmd = ['exec', args.services[0]]
|
||||
shell = _find_shell(config, args, compose_cmd)
|
||||
def _run_services(self, runner, args, project, services):
|
||||
service = services[0]
|
||||
shell = _find_shell(args, project, service)
|
||||
|
||||
if shell is not None:
|
||||
# spawn shell
|
||||
DockerCommand('docker-compose').run(
|
||||
config, args, [*compose_cmd, shell]
|
||||
)
|
||||
DockerCommand('docker-compose').run(project, [
|
||||
'exec', service, shell
|
||||
])
|
||||
return True
|
||||
|
||||
return False
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
# local
|
||||
from ._subcommand import SubCommand
|
||||
|
||||
# parent
|
||||
from ..config import LoadedConfig
|
||||
|
||||
|
||||
class ShowCommand(SubCommand):
|
||||
"""kiwi show"""
|
||||
|
@ -11,6 +14,6 @@ class ShowCommand(SubCommand):
|
|||
description="Show effective kiwi.yml"
|
||||
)
|
||||
|
||||
def run(self, runner, config, args):
|
||||
print(config)
|
||||
def _run_instance(self, runner, args):
|
||||
print(LoadedConfig.get())
|
||||
return True
|
||||
|
|
|
@ -1,28 +1,29 @@
|
|||
# local
|
||||
from ._subcommand import FlexCommand
|
||||
from ._subcommand import ServiceCommand
|
||||
from .utils.dockercommand import DockerCommand
|
||||
|
||||
|
||||
class UpCommand(FlexCommand):
|
||||
class UpCommand(ServiceCommand):
|
||||
"""kiwi up"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
'up', "Bringing up",
|
||||
'up', num_projects='?', num_services='*',
|
||||
action="Bringing up",
|
||||
description="Bring up the whole instance, a project or service(s) inside a project"
|
||||
)
|
||||
|
||||
def _run_instance(self, runner, config, args):
|
||||
def _run_instance(self, runner, args):
|
||||
if runner.run('conf-copy'):
|
||||
return super()._run_instance(runner, config, args)
|
||||
return super()._run_instance(runner, args)
|
||||
|
||||
return False
|
||||
|
||||
def _run_services(self, runner, config, args, services):
|
||||
def _run_services(self, runner, args, project, services):
|
||||
if runner.run('net-up'):
|
||||
DockerCommand('docker-compose').run(
|
||||
config, args, ['up', '-d', *services]
|
||||
)
|
||||
DockerCommand('docker-compose').run(project, [
|
||||
'up', '-d', *services
|
||||
])
|
||||
return True
|
||||
|
||||
return False
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
# local
|
||||
from ._subcommand import FlexCommand
|
||||
from ._subcommand import ServiceCommand
|
||||
from .utils.misc import are_you_sure
|
||||
|
||||
|
||||
class UpdateCommand(FlexCommand):
|
||||
class UpdateCommand(ServiceCommand):
|
||||
"""kiwi update"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
'update', "Updating",
|
||||
'update', num_projects='?', num_services='*',
|
||||
action="Updating",
|
||||
description="Update the whole instance, a project or service(s) inside a project"
|
||||
)
|
||||
|
||||
def _run_instance(self, runner, config, args):
|
||||
def _run_instance(self, runner, args):
|
||||
if are_you_sure([
|
||||
"This will update the entire instance at once.",
|
||||
"",
|
||||
|
@ -20,12 +21,14 @@ class UpdateCommand(FlexCommand):
|
|||
" - Updates may take a long time",
|
||||
" - Updates may break beloved functionality",
|
||||
]):
|
||||
return super()._run_instance(runner, config, args)
|
||||
return super()._run_instance(runner, args)
|
||||
|
||||
return False
|
||||
|
||||
def _run_services(self, runner, config, args, services):
|
||||
result = runner.run('build')
|
||||
def _run_services(self, runner, args, project, services):
|
||||
result = True
|
||||
|
||||
result &= runner.run('build')
|
||||
result &= runner.run('pull')
|
||||
result &= runner.run('conf-copy')
|
||||
result &= runner.run('down')
|
||||
|
|
|
@ -5,21 +5,16 @@ import subprocess
|
|||
|
||||
# local
|
||||
from .executable import Executable
|
||||
from .project import Projects
|
||||
|
||||
# parent
|
||||
from ..._constants import CONF_DIRECTORY_NAME
|
||||
from ...parser import Parser
|
||||
from ...config import LoadedConfig
|
||||
|
||||
|
||||
def _update_kwargs(**kwargs):
|
||||
config = LoadedConfig.get()
|
||||
projects = Projects.from_args(Parser().get_args())
|
||||
|
||||
# project given in args: command affects a project in this instance
|
||||
if projects:
|
||||
project = projects[0]
|
||||
def _update_kwargs(project, **kwargs):
|
||||
# enabled project given: command affects a project in this instance
|
||||
if project is not None and project.is_enabled():
|
||||
config = LoadedConfig.get()
|
||||
|
||||
# execute command in project directory
|
||||
kwargs['cwd'] = project.dir_name()
|
||||
|
@ -58,19 +53,15 @@ class DockerCommand(Executable):
|
|||
except subprocess.CalledProcessError:
|
||||
raise PermissionError("Cannot access docker, please get into the docker group or run as root!")
|
||||
|
||||
def run(self, config, args, process_args, **kwargs):
|
||||
kwargs = _update_kwargs(**kwargs)
|
||||
|
||||
def run(self, project, process_args, **kwargs):
|
||||
# equivalent to 'super().run' but agnostic of nested class construct
|
||||
return super().__getattr__("run")(
|
||||
process_args, config,
|
||||
**kwargs
|
||||
process_args,
|
||||
**_update_kwargs(project, **kwargs)
|
||||
)
|
||||
|
||||
def run_less(self, config, args, process_args, **kwargs):
|
||||
kwargs = _update_kwargs(**kwargs)
|
||||
|
||||
def run_less(self, project, process_args, **kwargs):
|
||||
return super().__getattr__("run_less")(
|
||||
process_args, config,
|
||||
**kwargs
|
||||
process_args,
|
||||
**_update_kwargs(project, **kwargs)
|
||||
)
|
||||
|
|
|
@ -3,18 +3,22 @@ import logging
|
|||
import os
|
||||
import subprocess
|
||||
|
||||
# parent
|
||||
from ...config import LoadedConfig
|
||||
|
||||
def _update_kwargs(config, **kwargs):
|
||||
if config is not None:
|
||||
# ensure there is an environment
|
||||
if 'env' not in kwargs:
|
||||
kwargs['env'] = {}
|
||||
|
||||
# add common environment from config
|
||||
if config['runtime:env'] is not None:
|
||||
kwargs['env'].update(config['runtime:env'])
|
||||
def _update_kwargs(**kwargs):
|
||||
config = LoadedConfig.get()
|
||||
|
||||
logging.debug(f"kwargs updated: {kwargs}")
|
||||
# ensure there is an environment
|
||||
if 'env' not in kwargs:
|
||||
kwargs['env'] = {}
|
||||
|
||||
# add common environment from config
|
||||
if config['runtime:env'] is not None:
|
||||
kwargs['env'].update(config['runtime:env'])
|
||||
|
||||
logging.debug(f"kwargs updated: {kwargs}")
|
||||
|
||||
return kwargs
|
||||
|
||||
|
@ -48,28 +52,24 @@ class Executable:
|
|||
logging.debug(f"Executable cmd{cmd}, kwargs{kwargs}")
|
||||
return cmd
|
||||
|
||||
def run(self, process_args, config=None, **kwargs):
|
||||
kwargs = _update_kwargs(config, **kwargs)
|
||||
|
||||
def run(self, process_args, **kwargs):
|
||||
return subprocess.run(
|
||||
self.__build_cmd(process_args, **kwargs),
|
||||
self.__build_cmd(process_args, **_update_kwargs(**kwargs)),
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def Popen(self, process_args, config=None, **kwargs):
|
||||
kwargs = _update_kwargs(config, **kwargs)
|
||||
|
||||
def Popen(self, process_args, **kwargs):
|
||||
return subprocess.Popen(
|
||||
self.__build_cmd(process_args, **kwargs),
|
||||
self.__build_cmd(process_args, **_update_kwargs(**kwargs)),
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def run_less(self, process_args, config=None, **kwargs):
|
||||
def run_less(self, process_args, **kwargs):
|
||||
kwargs['stdout'] = subprocess.PIPE
|
||||
kwargs['stderr'] = subprocess.DEVNULL
|
||||
|
||||
process = self.Popen(
|
||||
process_args, config,
|
||||
process_args,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
|
|
|
@ -11,6 +11,20 @@ class Project:
|
|||
def __init__(self, name):
|
||||
self.__name = name
|
||||
|
||||
@classmethod
|
||||
def from_file_name(cls, file_name):
|
||||
if os.path.isdir(file_name):
|
||||
config = LoadedConfig.get()
|
||||
|
||||
if file_name.endswith(config['markers:disabled']):
|
||||
file_name = file_name[:-len(config['markers:disabled'])]
|
||||
|
||||
if file_name.endswith(config['markers:project']):
|
||||
file_name = file_name[:-len(config['markers:project'])]
|
||||
return cls(file_name)
|
||||
|
||||
return None
|
||||
|
||||
def get_name(self):
|
||||
return self.__name
|
||||
|
||||
|
@ -78,40 +92,41 @@ class Project:
|
|||
return True
|
||||
|
||||
|
||||
def _extract_project_name(file_name):
|
||||
config = LoadedConfig.get()
|
||||
enabled_suffix = config['markers:project']
|
||||
disabled_suffix = f"{enabled_suffix}{config['markers:disabled']}"
|
||||
|
||||
if os.path.isdir(file_name):
|
||||
# all subdirectories
|
||||
if file_name.endswith(enabled_suffix):
|
||||
# enabled projects
|
||||
return file_name[:-len(enabled_suffix)]
|
||||
|
||||
elif file_name.endswith(disabled_suffix):
|
||||
# disabled projects
|
||||
return file_name[:-len(disabled_suffix)]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class Projects:
|
||||
__projects = None
|
||||
|
||||
def __init__(self, names):
|
||||
self.__projects = [
|
||||
Project(name)
|
||||
for name in names if isinstance(name, str)
|
||||
]
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.__projects[item]
|
||||
|
||||
def __str__(self):
|
||||
return str([
|
||||
project.get_name()
|
||||
for project
|
||||
in self.__projects
|
||||
])
|
||||
|
||||
@classmethod
|
||||
def all(cls):
|
||||
return cls([
|
||||
_extract_project_name(file_name)
|
||||
def from_names(cls, project_names):
|
||||
result = cls()
|
||||
result.__projects = [
|
||||
Project(name)
|
||||
for name in project_names if isinstance(name, str)
|
||||
]
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def from_projects(cls, projects):
|
||||
result = cls()
|
||||
result.__projects = [
|
||||
project
|
||||
for project in projects if isinstance(project, Project)
|
||||
]
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def from_dir(cls):
|
||||
return cls.from_projects([
|
||||
Project.from_file_name(file_name)
|
||||
for file_name in os.listdir()
|
||||
])
|
||||
|
||||
|
@ -119,9 +134,39 @@ class Projects:
|
|||
def from_args(cls, args):
|
||||
if args is not None and 'projects' in args:
|
||||
if isinstance(args.projects, list) and args.projects:
|
||||
return cls(args.projects)
|
||||
return cls.from_names(args.projects)
|
||||
|
||||
elif isinstance(args.projects, str):
|
||||
return cls([args.projects])
|
||||
return cls.from_names([args.projects])
|
||||
|
||||
return []
|
||||
return cls()
|
||||
|
||||
def empty(self):
|
||||
return not self.__projects
|
||||
|
||||
def filter_exists(self):
|
||||
result = Projects()
|
||||
result.__projects = [
|
||||
project
|
||||
for project in self.__projects
|
||||
if project.exists()
|
||||
]
|
||||
return result
|
||||
|
||||
def filter_enabled(self):
|
||||
result = Projects()
|
||||
result.__projects = [
|
||||
project
|
||||
for project in self.__projects
|
||||
if project.is_enabled()
|
||||
]
|
||||
return result
|
||||
|
||||
def filter_disabled(self):
|
||||
result = Projects()
|
||||
result.__projects = [
|
||||
project
|
||||
for project in self.__projects
|
||||
if project.is_disabled()
|
||||
]
|
||||
return result
|
||||
|
|
|
@ -14,6 +14,7 @@ def _prefix_path(prefix, path):
|
|||
if isinstance(path, str):
|
||||
abs_path = os.path.abspath(path)
|
||||
return os.path.realpath(f"{prefix}/{abs_path}")
|
||||
|
||||
elif isinstance(path, list):
|
||||
return [_prefix_path(prefix, p) for p in path]
|
||||
|
||||
|
@ -36,55 +37,43 @@ class Rootkit:
|
|||
def __init__(self, image_tag=None):
|
||||
self.__image_tag = image_tag
|
||||
|
||||
def __exists(self, config, args):
|
||||
ps = DockerCommand('docker').run(
|
||||
config, args, [
|
||||
'images',
|
||||
'--filter', f"reference={_image_name(self.__image_tag)}",
|
||||
'--format', '{{.Repository}}:{{.Tag}}'
|
||||
],
|
||||
stdout=subprocess.PIPE
|
||||
)
|
||||
def __exists(self):
|
||||
ps = DockerCommand('docker').run(None, [
|
||||
'images',
|
||||
'--filter', f"reference={_image_name(self.__image_tag)}",
|
||||
'--format', '{{.Repository}}:{{.Tag}}'
|
||||
], stdout=subprocess.PIPE)
|
||||
|
||||
return str(ps.stdout, 'utf-8').strip() == _image_name(self.__image_tag)
|
||||
|
||||
def __build_image(self, config, args):
|
||||
if self.__exists(config, args):
|
||||
def __build_image(self):
|
||||
if self.__exists():
|
||||
logging.info(f"Using image {_image_name(self.__image_tag)}")
|
||||
else:
|
||||
if self.__image_tag is None:
|
||||
logging.info(f"Pulling image {_image_name(self.__image_tag)}")
|
||||
DockerCommand('docker').run(
|
||||
config, args, ['pull', _image_name(self.__image_tag)],
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
|
||||
)
|
||||
DockerCommand('docker').run(None, [
|
||||
'pull', _image_name(self.__image_tag)
|
||||
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
|
||||
else:
|
||||
logging.info(f"Building image {_image_name(self.__image_tag)}")
|
||||
DockerCommand('docker').run(
|
||||
config, args,
|
||||
[
|
||||
'build',
|
||||
'-t', _image_name(self.__image_tag),
|
||||
'-f', f"{IMAGES_DIRECTORY_NAME}/{self.__image_tag}.Dockerfile",
|
||||
f"{IMAGES_DIRECTORY_NAME}"
|
||||
],
|
||||
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
|
||||
)
|
||||
DockerCommand('docker').run(None, [
|
||||
'build',
|
||||
'-t', _image_name(self.__image_tag),
|
||||
'-f', f"{IMAGES_DIRECTORY_NAME}/{self.__image_tag}.Dockerfile",
|
||||
f"{IMAGES_DIRECTORY_NAME}"
|
||||
], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
|
||||
def run(self, config, args, process_args, **kwargs):
|
||||
self.__build_image(config, args)
|
||||
DockerCommand('docker').run(
|
||||
config, args,
|
||||
[
|
||||
'run', '--rm',
|
||||
'-v', '/:/mnt',
|
||||
'-u', 'root',
|
||||
_image_name(self.__image_tag),
|
||||
*process_args
|
||||
],
|
||||
**kwargs
|
||||
)
|
||||
def run(self, process_args, **kwargs):
|
||||
self.__build_image()
|
||||
DockerCommand('docker').run(None, [
|
||||
'run', '--rm',
|
||||
'-v', '/:/mnt',
|
||||
'-u', 'root',
|
||||
_image_name(self.__image_tag),
|
||||
*process_args
|
||||
], **kwargs)
|
||||
|
||||
__image_tag = None
|
||||
__instances = {}
|
||||
|
|
Loading…
Reference in a new issue