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_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" /> <option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="kiwi_scp.scripts.kiwi_next" /> <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="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" /> <option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="true" /> <option name="MODULE_MODE" value="true" />

View file

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

View file

@ -1,13 +1,21 @@
import click import click
from kiwi_scp.misc import service_command from ..instance import Instance, pass_instance
from ..misc import service_command
@click.command( @click.command(
"list", "list",
short_help="Inspect a kiwi-scp instance", short_help="Inspect a kiwi-scp instance",
) )
@pass_instance
@service_command @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""" """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 functools
import re import re
from pathlib import Path from pathlib import Path
from typing import List, Dict, Any, Generator from typing import Generator, List
import attr import attr
import click import click
from ruamel.yaml.comments import CommentedMap
from ._constants import COMPOSE_FILE_NAME from ._constants import COMPOSE_FILE_NAME
from .config import KiwiConfig from .config import KiwiConfig
@ -16,27 +17,40 @@ _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()
configs: List[Path] = attr.ib() description: CommentedMap = attr.ib()
@classmethod def __str__(self) -> str:
def from_description(cls, name: str, description: Dict[str, Any]): return YAML().dump({
configs: List[Path] = [] "service": {
self.name: self.description
}
})
if "volumes" in description: @property
volumes: List[str] = description["volumes"] 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] host_part = volume.split(":")[0]
confdir = _RE_CONFDIR.match(host_part) cd_match = _RE_CONFDIR.match(host_part)
if confdir: if cd_match:
configs.append(Path(confdir.group(1))) 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 @attr.s
class Instance: class Instance:
@ -48,19 +62,24 @@ class Instance:
return KiwiConfig.from_directory(self.directory) return KiwiConfig.from_directory(self.directory)
@classmethod @staticmethod
@functools.lru_cache(maxsize=10) @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: with open(directory.joinpath(COMPOSE_FILE_NAME), "r") as cf:
return YAML().load(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)) yml = Instance._parse_compose_file(self.directory.joinpath(project_name))
return ( return Services(project_name, [
Service.from_description(name, description) Service(name, description)
for name, description in yml["services"].items() 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) pass_instance = click.make_pass_decorator(Instance, ensure=True)

View file

@ -14,9 +14,14 @@ class TestDefault:
assert p.name == "hello-world.project" 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): def test_empty(self):
i = Instance() i = Instance()