abandoned utils.misc for project handling

This commit is contained in:
Jörn-Michael Miehe 2020-08-19 11:58:13 +02:00
parent 0cf934965e
commit 267f85fa8d
9 changed files with 182 additions and 204 deletions

View file

@ -2,7 +2,7 @@
import logging import logging
# local # local
from .utils.misc import get_first_project_name, get_services, list_projects from .utils.project import Projects
# parent # parent
from ..parser import Parser from ..parser import Parser
@ -16,8 +16,9 @@ class SubCommand:
# command parser # command parser
_sub_parser = None _sub_parser = None
def __init__(self, name, **kwargs): def __init__(self, name, add_parser=True, **kwargs):
self.__name = name self.__name = name
if add_parser:
self._sub_parser = Parser().get_subparsers().add_parser( self._sub_parser = Parser().get_subparsers().add_parser(
name, name,
**kwargs **kwargs
@ -34,9 +35,9 @@ class SubCommand:
class ProjectCommand(SubCommand): class ProjectCommand(SubCommand):
"""this command concerns a project in current instance""" """this command concerns a project in current instance"""
def __init__(self, name, num_projects, **kwargs): def __init__(self, name, num_projects, add_parser=True, **kwargs):
super().__init__( super().__init__(
name, name, add_parser=add_parser,
**kwargs **kwargs
) )
@ -54,9 +55,9 @@ class ProjectCommand(SubCommand):
class ServiceCommand(ProjectCommand): class ServiceCommand(ProjectCommand):
"""this command concerns service(s) in a project""" """this command concerns service(s) in a project"""
def __init__(self, name, num_projects, num_services, **kwargs): def __init__(self, name, num_projects, num_services, add_parser=True, **kwargs):
super().__init__( super().__init__(
name, num_projects=num_projects, name, num_projects=num_projects, add_parser=add_parser,
**kwargs **kwargs
) )
@ -76,9 +77,9 @@ class FlexCommand(ServiceCommand):
__action = None __action = None
def __init__(self, name, action='', **kwargs): def __init__(self, name, action='', add_parser=True, **kwargs):
super().__init__( super().__init__(
name, num_projects='?', num_services='*', name, num_projects='?', num_services='*', add_parser=add_parser,
**kwargs **kwargs
) )
@ -91,8 +92,8 @@ class FlexCommand(ServiceCommand):
def _run_instance(self, runner, config, args): def _run_instance(self, runner, config, args):
result = True result = True
for project_name in list_projects(config): for project in Projects.from_args(args):
args.projects = project_name args.projects = project.get_name()
result &= runner.run(str(self)) result &= runner.run(str(self))
return result return result
@ -104,19 +105,18 @@ class FlexCommand(ServiceCommand):
pass pass
def run(self, runner, config, args): def run(self, runner, config, args):
project_name = get_first_project_name(args) projects = Projects.from_args(args)
services = get_services(args) if not projects:
if project_name is None:
# no project given, run for entire instance # no project given, run for entire instance
logging.info(f"{self.__action} this instance") logging.info(f"{self.__action} this instance")
return self._run_instance(runner, config, args) return self._run_instance(runner, config, args)
if not services: project = projects[0]
if args is None or 'services' not in args or not args.services:
# no services given, run for whole project # no services given, run for whole project
logging.info(f"{self.__action} project '{project_name}'") logging.info(f"{self.__action} project '{project.get_name()}'")
return self._run_project(runner, config, args) return self._run_project(runner, config, args)
# run for service(s) inside project # run for service(s) inside project
logging.info(f"{self.__action} services {services} in project '{project_name}'") logging.info(f"{self.__action} services {args.services} in project '{project.get_name()}'")
return self._run_services(runner, config, args, services) return self._run_services(runner, config, args, args.services)

View file

@ -5,7 +5,7 @@ import subprocess
# local # local
from ._subcommand import SubCommand from ._subcommand import SubCommand
from .utils.misc import list_projects, get_project_dir from .utils.project import Projects
from .utils.rootkit import Rootkit, prefix_path_mnt from .utils.rootkit import Rootkit, prefix_path_mnt
# parent # parent
@ -22,13 +22,11 @@ class ConfCopyCommand(SubCommand):
) )
def run(self, runner, config, args): def run(self, runner, config, args):
conf_dirs = [] conf_dirs = [
project.conf_dir_name()
for project_name in list_projects(config): for project in Projects.all()
project_conf = f"{get_project_dir(config, project_name)}/{CONF_DIRECTORY_NAME}" if project.is_enabled()
]
if os.path.isdir(project_conf):
conf_dirs.append(project_conf)
if conf_dirs: if conf_dirs:
# add target directory # add target directory
@ -76,14 +74,11 @@ class ConfCleanCommand(SubCommand):
def run(self, runner, config, args): def run(self, runner, config, args):
result = True result = True
# down all projects with config directories affected_projects = [
affected_projects = [] project.conf_dir_name()
for project in Projects.all()
for project_name in list_projects(config): if project.has_configs()
project_conf = f"{get_project_dir(config, project_name)}/{CONF_DIRECTORY_NAME}" ]
if os.path.isdir(project_conf):
affected_projects.append(project_name)
for project_name in affected_projects: for project_name in affected_projects:
args.projects = project_name args.projects = project_name
@ -91,7 +86,7 @@ class ConfCleanCommand(SubCommand):
# cleanly sync configs # cleanly sync configs
result &= runner.run('conf-purge') result &= runner.run('conf-purge')
result &= runner.run('conf-purge') result &= runner.run('conf-copy')
# bring projects back up # bring projects back up
for project_name in affected_projects: for project_name in affected_projects:

View file

@ -1,5 +1,5 @@
# local # local
from .utils.project import Project from .utils.project import Projects
from ._subcommand import ProjectCommand from ._subcommand import ProjectCommand
@ -13,9 +13,7 @@ class DisableCommand(ProjectCommand):
) )
def run(self, runner, config, args): def run(self, runner, config, args):
result = True return all([
project.disable()
for project in Project.from_args(args): for project in Projects.from_args(args)
result = project.disable() ])
return result

View file

@ -1,5 +1,5 @@
# local # local
from .utils.project import Project from .utils.project import Projects
from ._subcommand import ProjectCommand from ._subcommand import ProjectCommand
@ -13,9 +13,7 @@ class EnableCommand(ProjectCommand):
) )
def run(self, runner, config, args): def run(self, runner, config, args):
result = True return all([
project.enable()
for project in Project.from_args(args): for project in Projects.from_args(args)
result = project.enable() ])
return result

View file

@ -2,11 +2,12 @@
import logging import logging
import os import os
import subprocess import subprocess
import yaml
# local # local
from ._subcommand import FlexCommand from ._subcommand import FlexCommand
from .utils.dockercommand import DockerCommand from .utils.dockercommand import DockerCommand
from .utils.misc import list_projects, get_first_project_name, get_project_dir from .utils.project import Projects
def _print_list(strings): def _print_list(strings):
@ -31,35 +32,61 @@ class ListCommand(FlexCommand):
) )
def _run_instance(self, runner, config, args): def _run_instance(self, runner, config, args):
print(f"Projects in instance {os.getcwd()}:") print(f"kiwi-config instance at '{os.getcwd()}'")
print("") print("#########")
projects = Projects.all()
_print_list(list_projects(config)) enableds = [
project.get_name()
for project in projects
if project.is_enabled()
]
if enableds:
print(f"Enabled projects:")
_print_list(enableds)
disableds = [
project.get_name()
for project in projects
if project.is_disabled()
]
if disableds:
print(f"Disabled projects:")
_print_list(disableds)
return True return True
def _run_project(self, runner, config, args): def _run_project(self, runner, config, args):
project_name = get_first_project_name(args) project = Projects.from_args(args)[0]
print(f"Services in project '{project_name}':")
print("") if not project.exists():
logging.error(f"Project '{project.get_name()}' not found")
return False
print(f"Services in project '{project.get_name()}':")
print("#########")
ps = DockerCommand('docker-compose').run( ps = DockerCommand('docker-compose').run(
config, args, ['config', '--services'], config, args, ['config', '--services'],
stdout=subprocess.PIPE stdout=subprocess.PIPE
) )
_print_list(ps.stdout)
_print_list(ps.stdout)
return True return True
def _run_services(self, runner, config, args, services): def _run_services(self, runner, config, args, services):
import yaml project = Projects.from_args(args)[0]
project_name = get_first_project_name(args) if not project.exists():
project_dir = get_project_dir(config, project_name) logging.error(f"Project '{project.get_name()}' not found")
print(f"Configuration of services {services} in project '{project_name}':") return False
print("")
with open(os.path.join(project_dir, 'docker-compose.yml'), 'r') as stream: print(f"Configuration of services {services} in project '{project.get_name()}':")
print("#########")
with open(project.compose_file_name(), 'r') as stream:
try: try:
docker_compose_yml = yaml.safe_load(stream) docker_compose_yml = yaml.safe_load(stream)

View file

@ -4,7 +4,7 @@ import os
import shutil import shutil
# local # local
from .utils.misc import get_project_names, get_project_dir, get_project_down_dir from .utils.project import Projects
from ._subcommand import ProjectCommand from ._subcommand import ProjectCommand
# parent # parent
@ -22,18 +22,16 @@ class NewCommand(ProjectCommand):
def run(self, runner, config, args): def run(self, runner, config, args):
result = True result = True
projects = Projects.from_args(args)
for project_name in get_project_names(args): for project in projects:
project_dir = get_project_dir(config, project_name) if project.exists():
project_down_dir = get_project_down_dir(config, project_name) logging.error(f"Project '{project.get_name()}' exists in this instance!")
if os.path.isdir(project_dir) or os.path.isdir(project_down_dir):
logging.error(f"Project '{project_name}' exists in this instance!")
result = False result = False
else: else:
logging.info(f"Creating project '{project_name}'") logging.info(f"Creating project '{project.get_name()}'")
os.mkdir(project_dir) os.mkdir(project.enabled_dir_name())
shutil.copy(DEFAULT_DOCKER_COMPOSE_NAME, os.path.join(project_dir, "docker-compose.yml")) shutil.copy(DEFAULT_DOCKER_COMPOSE_NAME, project.compose_file_name())
return result return result

View file

@ -5,18 +5,24 @@ import subprocess
# local # local
from .executable import Executable from .executable import Executable
from .misc import get_project_dir, get_first_project_name from .project import Projects
# parent # parent
from ..._constants import CONF_DIRECTORY_NAME from ..._constants import CONF_DIRECTORY_NAME
from ...parser import Parser
from ...config import LoadedConfig
def _update_kwargs(config, args, **kwargs): 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 # project given in args: command affects a project in this instance
project_name = get_first_project_name(args) if projects:
if project_name is not None: project = projects[0]
# execute command in project directory # execute command in project directory
kwargs['cwd'] = get_project_dir(config, project_name) kwargs['cwd'] = project.dir_name()
# ensure there is an environment # ensure there is an environment
if 'env' not in kwargs: if 'env' not in kwargs:
@ -24,10 +30,10 @@ def _update_kwargs(config, args, **kwargs):
# create environment variables for docker commands # create environment variables for docker commands
kwargs['env'].update({ kwargs['env'].update({
'COMPOSE_PROJECT_NAME': project_name, 'COMPOSE_PROJECT_NAME': project.get_name(),
'KIWI_HUB_NAME': config['network:name'], 'KIWI_HUB_NAME': config['network:name'],
'CONFDIR': os.path.join(config['runtime:storage'], CONF_DIRECTORY_NAME), 'CONFDIR': os.path.join(config['runtime:storage'], CONF_DIRECTORY_NAME),
'TARGETDIR': os.path.join(config['runtime:storage'], get_project_dir(config, project_name)) 'TARGETDIR': project.target_dir_name()
}) })
logging.debug(f"kwargs updated: {kwargs}") logging.debug(f"kwargs updated: {kwargs}")
@ -53,7 +59,7 @@ class DockerCommand(Executable):
raise PermissionError("Cannot access docker, please get into the docker group or run as root!") raise PermissionError("Cannot access docker, please get into the docker group or run as root!")
def run(self, config, args, process_args, **kwargs): def run(self, config, args, process_args, **kwargs):
kwargs = _update_kwargs(config, args, **kwargs) kwargs = _update_kwargs(**kwargs)
# equivalent to 'super().run' but agnostic of nested class construct # equivalent to 'super().run' but agnostic of nested class construct
return super().__getattr__("run")( return super().__getattr__("run")(
@ -62,7 +68,7 @@ class DockerCommand(Executable):
) )
def run_less(self, config, args, process_args, **kwargs): def run_less(self, config, args, process_args, **kwargs):
kwargs = _update_kwargs(config, args, **kwargs) kwargs = _update_kwargs(**kwargs)
return super().__getattr__("run_less")( return super().__getattr__("run_less")(
process_args, config, process_args, config,

View file

@ -1,76 +1,3 @@
import os
def get_project_names(args):
"""get project names from CLI args"""
if args is not None and 'projects' in args:
if isinstance(args.projects, list):
if args.projects:
return args.projects
else:
return None
elif isinstance(args.projects, str):
return [args.projects]
return None
def get_first_project_name(args):
"""get first project name from CLI args"""
names = get_project_names(args)
if names is not None:
return names[0]
return None
def get_services(args):
"""get services list from CLI args"""
if args is not None and 'services' in args:
return args.services
return None
def get_project_dir(config, project_name):
"""get project directory"""
return f"{project_name}{config['markers:project']}"
def get_project_down_dir(config, project_name):
"""get project directory"""
return f"{get_project_dir(config, project_name)}{config['markers:down']}"
def get_target_dir(config, project_name):
"""get project's target directory"""
return os.path.join(config['runtime:storage'], get_project_dir(config, project_name))
def list_projects(config):
"""list projects in current instance"""
# complete dir listing
content = os.listdir()
# filter directories
dirs = [d for d in content if os.path.isdir(d)]
# filter suffix
project_dirs = [p for p in dirs if p.endswith(config['markers:project'])]
# remove suffix
projects = [p[:-len(config['markers:project'])] for p in project_dirs]
return projects
def _surround(string, bang): def _surround(string, bang):
midlane = f"{bang * 3} {string} {bang * 3}" midlane = f"{bang * 3} {string} {bang * 3}"
sidelane = bang*len(midlane) sidelane = bang*len(midlane)
@ -86,6 +13,7 @@ def _emphasize(lines):
else: else:
return lines return lines
def are_you_sure(prompt, default="no"): def are_you_sure(prompt, default="no"):
if default.lower() == 'yes': if default.lower() == 'yes':
suffix = "[YES|no]" suffix = "[YES|no]"

View file

@ -1,79 +1,58 @@
import logging import logging
import os import os
from kiwi._constants import CONF_DIRECTORY_NAME from ..._constants import CONF_DIRECTORY_NAME
from kiwi.config import LoadedConfig from ...config import LoadedConfig
class Project: class Project:
__name = None __name = None
__config = None
def __init__(self, name): def __init__(self, name):
self.__name = name self.__name = name
self.__config = LoadedConfig.get()
@classmethod
def from_names(cls, names):
return [cls(name) for name in names]
@classmethod
def all(cls):
# current directory content
content = os.listdir()
# filter subdirectories
dirs = [dir_name for dir_name in content if os.path.isdir(dir_name)]
# filter by suffix
project_dirs = [dir_name for dir_name in dirs if dir_name.endswith(cls.__config['markers:project'])]
# remove suffix
project_names = [project_name[:-len(cls.__config['markers:project'])] for project_name in project_dirs]
return cls.from_names(project_names)
@classmethod
def from_args(cls, args):
if args is not None and 'projects' in args:
if isinstance(args.projects, list) and args.projects:
return cls.from_names(args.projects)
elif isinstance(args.projects, str):
return cls.from_names([args.projects])
return []
def get_name(self): def get_name(self):
return self.__name return self.__name
def dir_name(self): def dir_name(self):
return f"{self.__name}{self.__config['markers:project']}" if self.is_enabled():
return self.enabled_dir_name()
elif self.is_disabled():
return self.disabled_dir_name()
else:
return None
def down_dir_name(self): def enabled_dir_name(self):
return f"{self.dir_name()}{self.__config['markers:down']}" return f"{self.__name}{LoadedConfig.get()['markers:project']}"
def disabled_dir_name(self):
return f"{self.enabled_dir_name()}{LoadedConfig.get()['markers:down']}"
def conf_dir_name(self): def conf_dir_name(self):
return os.path.join(self.dir_name(), CONF_DIRECTORY_NAME) return os.path.join(self.dir_name(), CONF_DIRECTORY_NAME)
def compose_file_name(self):
return os.path.join(self.dir_name(), 'docker-compose.yml')
def target_dir_name(self): def target_dir_name(self):
return os.path.join(self.__config['runtime:storage'], self.dir_name()) return os.path.join(LoadedConfig.get()['runtime:storage'], self.enabled_dir_name())
def exists(self): def exists(self):
return os.path.isdir(self.dir_name()) or os.path.isdir(self.down_dir_name()) return os.path.isdir(self.enabled_dir_name()) or os.path.isdir(self.disabled_dir_name())
def is_enabled(self): def is_enabled(self):
return os.path.isdir(self.dir_name()) return os.path.isdir(self.enabled_dir_name())
def is_disabled(self): def is_disabled(self):
return os.path.isdir(self.down_dir_name()) return os.path.isdir(self.disabled_dir_name())
def has_configs(self): def has_configs(self):
return os.path.isdir(self.dir_name()) return os.path.isdir(self.conf_dir_name())
def enable(self): def enable(self):
if self.is_disabled(): if self.is_disabled():
logging.info(f"Enabling project '{self.get_name()}'") logging.info(f"Enabling project '{self.get_name()}'")
os.rename(self.down_dir_name(), self.dir_name()) os.rename(self.dir_name(), self.enabled_dir_name())
elif self.is_enabled(): elif self.is_enabled():
logging.warning(f"Project '{self.get_name()}' is enabled!") logging.warning(f"Project '{self.get_name()}' is enabled!")
@ -87,7 +66,7 @@ class Project:
def disable(self): def disable(self):
if self.is_enabled(): if self.is_enabled():
logging.info(f"Disabling project '{self.get_name()}'") logging.info(f"Disabling project '{self.get_name()}'")
os.rename(self.dir_name(), self.down_dir_name()) os.rename(self.dir_name(), self.disabled_dir_name())
elif self.is_disabled(): elif self.is_disabled():
logging.warning(f"Project '{self.get_name()}' is disabled!") logging.warning(f"Project '{self.get_name()}' is disabled!")
@ -97,3 +76,52 @@ class Project:
return False return False
return True return True
def _extract_project_name(file_name):
config = LoadedConfig.get()
enabled_suffix = config['markers:project']
disabled_suffix = f"{enabled_suffix}{config['markers:down']}"
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]
@classmethod
def all(cls):
return cls([
_extract_project_name(file_name)
for file_name in os.listdir()
])
@classmethod
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)
elif isinstance(args.projects, str):
return cls([args.projects])
return []