basic "kiwi list" command

This commit is contained in:
Jörn-Michael Miehe 2021-10-28 15:53:32 +02:00
parent 1dcf542c6d
commit a5a0ca9a63
5 changed files with 65 additions and 33 deletions

View file

@ -12,7 +12,7 @@
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="kiwi_scp.scripts.kiwi_next" />
<option name="PARAMETERS" value="list" />
<option name="PARAMETERS" value="list hello-world.project web" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="true" />

View file

@ -10,20 +10,20 @@ networks:
name: ${KIWI_HUB_NAME}
services:
# simple loop producing (rather boring) logs
greeter:
# simple loop producing (rather boring) logs
image: alpine:latest
command: sh -c 'LOOP=1; while :; do echo Hello World "$$LOOP"; LOOP=$$(($$LOOP + 1)); sleep 10; done'
# basic webserver listening on localhost:8080
web:
# basic webserver listening on localhost:8080
build: web
restart: unless-stopped
ports:
- "8080:80"
# internal mariadb (mysql) instance with persistent storage
db:
# internal mariadb (mysql) instance with persistent storage
image: mariadb:10
restart: unless-stopped
networks:
@ -33,8 +33,8 @@ services:
volumes:
- "${TARGETDIR}/db:/var/lib/mysql"
# admin interface for databases
adminer:
# admin interface for databases
image: adminer:standalone
restart: unless-stopped
networks:
@ -45,8 +45,8 @@ services:
ports:
- "8081:8080"
# Another webserver just to show off the ${CONFDIR} variable
another-web:
# Another webserver just to show off the ${CONFDIR} variable
image: nginx:stable-alpine
restart: unless-stopped
ports:

View file

@ -1,13 +1,21 @@
import click
from kiwi_scp.misc import service_command
from ..instance import Instance, pass_instance
from ..misc import service_command
@click.command(
"list",
short_help="Inspect a kiwi-scp instance",
)
@pass_instance
@service_command
def cmd(project: str, service: str):
def cmd(ctx: Instance, project: str, service: str):
"""List projects in this instance, services inside a project or service(s) inside a project"""
print(f"project: {project!r}, service: {service!r}")
if project is not None:
if service is not None:
print(f"{ctx.get_service(project, service)}")
else:
print(f"services: {ctx.get_services(project)}")
else:
print(f"projects: {ctx.config.projects}")

View file

@ -1,10 +1,11 @@
import functools
import re
from pathlib import Path
from typing import List, Dict, Any, Generator
from typing import Generator, List
import attr
import click
from ruamel.yaml.comments import CommentedMap
from ._constants import COMPOSE_FILE_NAME
from .config import KiwiConfig
@ -16,27 +17,40 @@ _RE_CONFDIR = re.compile(r"^\s*\$(?:CONFDIR|{CONFDIR})/+(.*)$", flags=re.UNICODE
@attr.s
class Service:
name: str = attr.ib()
configs: List[Path] = attr.ib()
description: CommentedMap = attr.ib()
@classmethod
def from_description(cls, name: str, description: Dict[str, Any]):
configs: List[Path] = []
def __str__(self) -> str:
return YAML().dump({
"service": {
self.name: self.description
}
})
if "volumes" in description:
volumes: List[str] = description["volumes"]
@property
def configs(self) -> Generator[Path, None, None]:
if "volumes" not in self.description:
return
for volume in volumes:
for volume in self.description["volumes"]:
host_part = volume.split(":")[0]
confdir = _RE_CONFDIR.match(host_part)
cd_match = _RE_CONFDIR.match(host_part)
if confdir:
configs.append(Path(confdir.group(1)))
if cd_match:
yield cd_match.group(1)
return cls(
name=name,
configs=configs,
)
@attr.s
class Services:
project_name: str = attr.ib()
content: List[Service] = attr.ib()
def __str__(self) -> str:
return YAML().dump({
"services": {
service.name: service.description
for service in self.content
}
})
@attr.s
class Instance:
@ -48,19 +62,24 @@ class Instance:
return KiwiConfig.from_directory(self.directory)
@classmethod
@staticmethod
@functools.lru_cache(maxsize=10)
def _parse_compose_file(cls, directory: Path):
def _parse_compose_file(directory: Path):
with open(directory.joinpath(COMPOSE_FILE_NAME), "r") as cf:
return YAML().load(cf)
def get_services(self, project_name: str) -> Generator[Service, None, None]:
def get_services(self, project_name: str) -> Services:
yml = Instance._parse_compose_file(self.directory.joinpath(project_name))
return (
Service.from_description(name, description)
return Services(project_name, [
Service(name, description)
for name, description in yml["services"].items()
)
])
def get_service(self, project_name: str, service_name: str) -> Service:
yml = Instance._parse_compose_file(self.directory.joinpath(project_name))
return Service(service_name, yml["services"][service_name])
pass_instance = click.make_pass_decorator(Instance, ensure=True)

View file

@ -14,9 +14,14 @@ class TestDefault:
assert p.name == "hello-world.project"
s = list(i.get_services(p.name))
ss = list(i.get_services(p.name))
assert len(s) == 5
assert len(ss) == 5
s = ss[0]
assert s.name == "greeter"
assert s == i.get_service(p.name, s.name)
def test_empty(self):
i = Instance()