"kiwi list" done

This commit is contained in:
Jörn-Michael Miehe 2021-11-03 16:32:01 +01:00
parent 1f588d5364
commit 3686731f29
3 changed files with 109 additions and 50 deletions

View file

@ -1,6 +1,7 @@
import os import os
import sys
from enum import Enum, auto from enum import Enum, auto
from typing import List from typing import List, Tuple, Iterable
import click import click
@ -30,6 +31,28 @@ class KiwiCLI(click.MultiCommand):
class KiwiCommand: class KiwiCommand:
@staticmethod
def print_multi_color(*content: Tuple[str, str]):
for message, color in content:
click.secho(message, fg=color, nl=False)
click.echo()
@staticmethod
def print_header(header: str):
click.secho(header, fg="green", bold=True)
@staticmethod
def print_error(header: str):
click.secho(header, file=sys.stderr, fg="red", bold=True)
@staticmethod
def print_list(content: Iterable[str]):
for item in content:
KiwiCommand.print_multi_color(
(" - ", "green"),
(item, "blue"),
)
@classmethod @classmethod
def run_for_instance(cls, instance: Instance, **kwargs) -> None: def run_for_instance(cls, instance: Instance, **kwargs) -> None:
for project in instance.config.projects: for project in instance.config.projects:
@ -37,11 +60,18 @@ class KiwiCommand:
@classmethod @classmethod
def run_for_project(cls, instance: Instance, project_name: str, **kwargs) -> None: def run_for_project(cls, instance: Instance, project_name: str, **kwargs) -> None:
service_names = [service.name for service in instance.get_services(project_name, None).content] project = instance.get_project(project_name)
if project is None:
click.secho(f"No project '{project_name}' in kiwi-scp instance at '{instance.directory}'.", fg="red", bold=True)
return
service_names = [service.name for service in project.get_services().content]
cls.run_for_services(instance, project_name, service_names, **kwargs) cls.run_for_services(instance, project_name, service_names, **kwargs)
@classmethod @classmethod
def run_for_services(cls, instance: Instance, project_name: str, services: List[str], **kwargs) -> None: def run_for_services(cls, instance: Instance, project_name: str, service_names: List[str], **kwargs) -> None:
pass pass

View file

@ -21,23 +21,50 @@ class CMD(KiwiCommand):
"""List projects in this instance, services inside a project or service(s) inside a project""" """List projects in this instance, services inside a project or service(s) inside a project"""
@classmethod @classmethod
def run_for_instance(cls, instance: Instance, show: bool = None, **kwargs): def run_for_instance(cls, instance: Instance, show: bool = None, **kwargs) -> None:
if show: if show:
click.secho(f"Showing config for kiwi-scp instance at '{instance.directory}'.", fg="green", bold=True) KiwiCommand.print_header(f"Showing config for kiwi-scp instance at '{instance.directory}'.")
click.echo_via_pager(instance.config.kiwi_yml) click.echo_via_pager(instance.config.kiwi_yml)
else: else:
click.secho(f"Projects in kiwi-scp instance at '{instance.directory}':", fg="green", bold=True) KiwiCommand.print_header(f"Projects in kiwi-scp instance at '{instance.directory}':")
KiwiCommand.print_list(
for project in instance.config.projects: project.name + click.style(" (disabled)" if not project.enabled else "", fg="red")
click.echo( for project in instance.config.projects
click.style(" - ", fg="green") +
click.style(project.name, fg="blue") +
click.style(' (disabled)' if not project.enabled else '', fg="red")
) )
@classmethod @classmethod
def run_for_services(cls, instance: Instance, project_name: str, services: List[str], show: bool = None, def run_for_project(cls, instance: Instance, project_name: str, show: bool = None, **kwargs) -> None:
**kwargs): project = instance.get_project(project_name)
print(show)
print(services) if project is None:
KiwiCommand.print_error(f"No project '{project_name}' in kiwi-scp instance at '{instance.directory}'.")
return
services = project.get_services()
if show:
KiwiCommand.print_header(f"Showing config for all services in project '{project_name}'.")
click.echo_via_pager(str(services))
else:
KiwiCommand.print_header(f"Services in project '{project_name}':")
KiwiCommand.print_list(service.name for service in services.content)
@classmethod
def run_for_services(cls, instance: Instance, project_name: str, service_names: List[str], show: bool = None,
**kwargs) -> None:
project = instance.get_project(project_name)
if project is None:
KiwiCommand.print_error(f"No project '{project_name}' in kiwi-scp instance at '{instance.directory}'.")
return
services = project.get_services(service_names)
if show:
KiwiCommand.print_header(
f"Showing config for services '{', '.join(service_names)}' in project '{project_name}'.")
click.echo_via_pager(str(services))
else:
KiwiCommand.print_header(f"Matching services in project '{project_name}':")
KiwiCommand.print_list(service.name for service in services.content)

View file

@ -16,21 +16,14 @@ _RE_CONFDIR = re.compile(r"^\s*\$(?:CONFDIR|{CONFDIR})/+(.*)$", flags=re.UNICODE
@attr.s @attr.s
class Service: class Service:
name: str = attr.ib() name: str = attr.ib()
description: CommentedMap = attr.ib() content: CommentedMap = attr.ib()
def __str__(self) -> str:
return YAML().dump({
"service": {
self.name: self.description
}
}).strip()
@property @property
def configs(self) -> Generator[Path, None, None]: def configs(self) -> Generator[Path, None, None]:
if "volumes" not in self.description: if "volumes" not in self.content:
return return
for volume in self.description["volumes"]: for volume in self.content["volumes"]:
host_part = volume.split(":")[0] host_part = volume.split(":")[0]
cd_match = _RE_CONFDIR.match(host_part) cd_match = _RE_CONFDIR.match(host_part)
@ -40,18 +33,44 @@ class Service:
@attr.s @attr.s
class Services: class Services:
project_name: str = attr.ib()
content: List[Service] = attr.ib() content: List[Service] = attr.ib()
def __str__(self) -> str: def __str__(self) -> str:
return YAML().dump({ return YAML().dump({
"services": { "services": {
service.name: service.description service.name: service.content
for service in self.content for service in self.content
} }
}).strip() }).strip()
@attr.s
class Project:
directory: Path = attr.ib()
@staticmethod
@functools.lru_cache(maxsize=10)
def _parse_compose_file(directory: Path) -> CommentedMap:
with open(directory.joinpath(COMPOSE_FILE_NAME), "r") as cf:
return YAML().load(cf)
def get_services(self, service_names: Optional[List[str]] = None) -> Services:
yml = Project._parse_compose_file(self.directory)
services = [
Service(name, description)
for name, description in yml["services"].items()
]
if not service_names:
return Services(services)
else:
return Services([
service
for service in services
if service.name in service_names
])
@attr.s @attr.s
class Instance: class Instance:
directory: Path = attr.ib(default=Path('.')) directory: Path = attr.ib(default=Path('.'))
@ -62,24 +81,7 @@ class Instance:
return KiwiConfig.from_directory(self.directory) return KiwiConfig.from_directory(self.directory)
@staticmethod def get_project(self, project_name: str) -> Optional[Project]:
@functools.lru_cache(maxsize=10) for project in self.config.projects:
def _parse_compose_file(directory: Path): if project.name == project_name:
with open(directory.joinpath(COMPOSE_FILE_NAME), "r") as cf: return Project(self.directory.joinpath(project.name))
return YAML().load(cf)
def get_services(self, project_name: str, service_names: Optional[Tuple[str]] = None) -> Services:
yml = Instance._parse_compose_file(self.directory.joinpath(project_name))
services = [
Service(name, description)
for name, description in yml["services"].items()
]
if not service_names:
return Services(project_name, services)
else:
return Services(project_name, [
service
for service in services
if service.name in service_names
])