Skip to content

Commit

Permalink
add config cache
Browse files Browse the repository at this point in the history
  • Loading branch information
ice-black-tea committed Mar 30, 2024
1 parent eb715a9 commit 1eaf7a9
Show file tree
Hide file tree
Showing 13 changed files with 393 additions and 271 deletions.
386 changes: 228 additions & 158 deletions src/linktools/_config.py

Large diffs are not rendered by default.

31 changes: 24 additions & 7 deletions src/linktools/_environ.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,34 +243,46 @@ def _default_config(self) -> "ConfigDict":
def _create_config(self) -> "Config":
from ._config import Config

return Config(self, self._default_config)
return Config(
self,
self._default_config,
namespace="MAIN",
prefix=f"{self.name.upper()}_",
)

@cached_property
def config(self) -> "Config":
"""
环境相关配置
"""
config = self._create_config()
config.load_from_env()
return config
return self._create_config()

def wrap_config(self) -> "Config":
def wrap_config(self, namespace: str = metadata.__missing__, prefix: str = metadata.__missing__) -> "Config":
"""
环境相关配置,与environ.config共享数据
环境相关配置,与environ.config共享配置数据,但不共享缓存数据和环境变量信息
:param namespace: 缓存对应的命名空间
:param prefix: 环境变量使用前缀
:return: 配置对象
"""
from ._config import ConfigWrapper

return ConfigWrapper(self.config)
return ConfigWrapper(self.config, namespace=namespace, prefix=prefix)

def get_config(self, key: str, type: "Type[T]" = None, default: Any = metadata.__missing__) -> "T":
"""
获取指定配置,优先会从环境变量中获取
:param key: 配置键
:param type: 配置类型
:param default: 默认值
:return: 配置值
"""
return self.config.get(key=key, type=type, default=default)

def set_config(self, key: str, value: Any) -> None:
"""
更新配置
:param key: 配置键
:param value: 配置值
"""
self.config.set(key, value)

Expand Down Expand Up @@ -306,6 +318,9 @@ def tools(self) -> "Tools":
def get_tool(self, name: str, **kwargs) -> "Tool":
"""
获取指定工具
:param name: 工具名
:param kwargs: 工具其他参数
:return: 工具对象
"""
tool = self.tools[name]
if len(kwargs) != 0:
Expand All @@ -315,6 +330,8 @@ def get_tool(self, name: str, **kwargs) -> "Tool":
def get_url_file(self, url: str) -> "UrlFile":
"""
获取指定url
:param url: url地址
:return: UrlFile对象
"""
from ._url import UrlFile

Expand Down
10 changes: 5 additions & 5 deletions src/linktools/_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,29 +235,29 @@ def config(self) -> dict:

# download url
download_url = utils.get_item(cfg, "download_url") or ""
if download_url is __missing__:
if download_url == __missing__:
download_url = ""
assert isinstance(download_url, str), \
f"Tool<{cfg['name']}>.download_url type error, " \
f"str was expects, got {type(download_url)}"
cfg["download_url"] = download_url.format(tools=self._container, **cfg)

unpack_path = utils.get_item(cfg, "unpack_path") or ""
if unpack_path is __missing__:
if unpack_path == __missing__:
unpack_path = ""
assert isinstance(unpack_path, str), \
f"Tool<{cfg['name']}>.unpack_path type error, " \
f"str was expects, got {type(unpack_path)}"

target_path = utils.get_item(cfg, "target_path") or ""
if target_path is __missing__:
if target_path == __missing__:
target_path = ""
assert isinstance(target_path, str), \
f"Tool<{cfg['name']}>.target_path type error, " \
f"str was expects, got {type(target_path)}"

absolute_path = utils.get_item(cfg, "absolute_path") or ""
if absolute_path is __missing__:
if absolute_path == __missing__:
absolute_path = ""
assert isinstance(absolute_path, str), \
f"Tool<{cfg['name']}>.absolute_path type error, " \
Expand Down Expand Up @@ -286,7 +286,7 @@ def config(self) -> dict:

# set executable cmdline
cmdline = utils.get_item(cfg, "cmdline") or ""
if cmdline is __missing__:
if cmdline == __missing__:
cmdline = cfg["name"]
assert isinstance(cmdline, str), \
f"Tool<{cfg['name']}>.cmdline type error, " \
Expand Down
15 changes: 10 additions & 5 deletions src/linktools/assets/containers/100-nginx/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import os
import re
import shutil
import textwrap

from linktools import Config, utils
from linktools.container import BaseContainer
Expand All @@ -52,11 +53,15 @@ def configs(self):
WILDCARD_DOMAIN=Config.Confirm(default=False, cached=True),
HTTP_PORT=Config.Prompt(default=80, type=int, cached=True),
HTTPS_PORT=Config.Prompt(default=443, type=int, cached=True),
ACME_DNS_API=Config.Sample({
"ACME_DNS_API": "dns_ali <= parameter --dns, find from https://github.com/acmesh-official/acme.sh/wiki/dnsapi",
"Ali_Key ": "<key> <= environment variable with dns_ali",
"Ali_Secret ": "<secret> <= environment variable with dns_ali",
})
ACME_DNS_API=Config.Error(textwrap.dedent(
"""
Ensure ACME_DNS_API config matches --dns parameter in acme command is set.
· Also, set corresponding environment variables.
· For details, see: https://github.com/acmesh-official/acme.sh/wiki/dnsapi.
· Example command:
$ ct-cntr config set ACME_DNS_API=dns_ali Ali_Key=xxx Ali_Secret=yyy
"""
))
)

@cached_property
Expand Down
6 changes: 3 additions & 3 deletions src/linktools/cli/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ def __call__(self, parser, namespace, values, option_string=None):


def _filter_kwargs(kwargs):
return {k: v for k, v in kwargs.items() if v is not __missing__}
return {k: v for k, v in kwargs.items() if v != __missing__}


_subcommand_index: int = 0
Expand Down Expand Up @@ -270,7 +270,7 @@ def decorator(func):

class _SubCommandInfo:

def __init__(self, subcommand: Union["SubCommand", "_SubCommandInfo"]):
def __init__(self, subcommand: "Union[SubCommand, _SubCommandInfo]"):
self.node: SubCommand = subcommand.node if isinstance(subcommand, _SubCommandInfo) else subcommand
self.children: List[_SubCommandInfo] = []

Expand Down Expand Up @@ -388,7 +388,7 @@ def create_parser(self, type: Callable[..., ArgumentParser]) -> ArgumentParser:
if dest not in signature.parameters:
raise SubCommandError(
f"Check subcommand argument error, "
f"{self.info} has no `{argument.action.dest}` argument")
f"{self.info} has no `{dest}` argument")

# 根据方法参数的注解,设置一些默认值
parameter = signature.parameters[dest]
Expand Down
61 changes: 45 additions & 16 deletions src/linktools/cli/commands/common/cntr.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,14 @@
"""
from argparse import Namespace, ArgumentParser
from subprocess import SubprocessError
from typing import Optional, List, Type
from typing import Optional, List, Type, Dict, Tuple

import yaml
from git import GitCommandError

from linktools import environ, ConfigError, utils
from linktools.cli import BaseCommand, subcommand, SubCommandWrapper, subcommand_argument, SubCommandGroup
from linktools.cli.argparse import KeyValueAction
from linktools.container import ContainerManager, ContainerError
from linktools.rich import confirm, choose

Expand Down Expand Up @@ -120,25 +122,36 @@ def run(self, args: Namespace) -> Optional[int]:
privilege=False,
).check_call()

@subcommand("set", help="set container configs")
@subcommand_argument("configs", action=KeyValueAction, nargs="+", help="container config key=value")
def on_command_set(self, configs: Dict[str, str]):
manager.config.save_cache(**configs)
for key in sorted(configs.keys()):
value = manager.config.get(key)
self.logger.info(f"{key}: {value}")

@subcommand("remove", help="remove container configs")
@subcommand_argument("keys", metavar="KEY", nargs="+", help="container config keys")
def on_command_remove(self, keys: List[str]):
manager.config.remove_cache(*keys)
self.logger.info(f"Remove {', '.join(keys)} success")

@subcommand("list", help="list container configs")
def on_command_list(self):
keys = set()
containers = manager.prepare_installed_containers()
for container in containers:
for container in manager.prepare_installed_containers():
keys.update(container.configs.keys())
if hasattr(container, "keys") and isinstance(container.keys, (Tuple, List)):
keys.update([key for key in container.keys if key in manager.config])
for key in sorted(keys):
value = manager.config.get(key)
self.logger.info(f"{key}: {value}")

@subcommand("reload", help="reload container configs")
def on_command_reload(self):
manager.config.set("RELOAD_CONFIG", True)
manager.config.reload = True
manager.prepare_installed_containers()

@subcommand("path", help="show config path")
def on_command_path(self):
print(manager.config.path, end="")


class ExecCommand(BaseCommand):
"""exec container command"""
Expand All @@ -147,14 +160,15 @@ class ExecCommand(BaseCommand):
def name(self):
return "exec"

def iter_installed_container_names(self):
@classmethod
def _iter_installed_container_names(cls):
containers = manager.get_installed_containers()
containers = manager.resolve_depend_containers(containers)
return [container.name for container in containers]

def init_arguments(self, parser: ArgumentParser) -> None:
parser.add_argument("exec_name", nargs="?", metavar="CONTAINER", help="container name",
choices=utils.lazy_load(self.iter_installed_container_names))
choices=utils.lazy_load(self._iter_installed_container_names))
parser.add_argument("exec_args", nargs="...", metavar="ARGS", help="container exec args")

def run(self, args: Namespace) -> Optional[int]:
Expand Down Expand Up @@ -189,7 +203,7 @@ class Command(BaseCommand):
@property
def known_errors(self) -> List[Type[BaseException]]:
known_errors = super().known_errors
known_errors.extend([ContainerError, ConfigError, SubprocessError])
known_errors.extend([ContainerError, ConfigError, SubprocessError, GitCommandError, OSError])
return known_errors

def init_arguments(self, parser: ArgumentParser) -> None:
Expand Down Expand Up @@ -265,38 +279,53 @@ def on_command_info(self, names: List[str]):
@subcommand("up", help="deploy installed containers")
def on_command_up(self):
containers = manager.prepare_installed_containers()

for container in containers:
container.on_starting()

manager.create_docker_compose_process(
containers,
"up", "-d", "--build", "--remove-orphans"
).check_call()

for container in reversed(containers):
container.on_started()

@subcommand("restart", help="restart installed containers")
def on_command_restart(self):
containers = manager.prepare_installed_containers()
for container in containers:
container.on_starting()

for container in reversed(containers):
container.on_stopping()
manager.create_docker_compose_process(
containers,
"restart"
"stop"
).check_call()
for container in containers:
container.on_stopped()

for container in containers:
container.on_starting()
manager.create_docker_compose_process(
containers,
"up", "-d", "--build", "--remove-orphans"
).check_call()
for container in reversed(containers):
container.on_started()

@subcommand("down", help="stop installed containers")
def on_command_down(self):
containers = manager.prepare_installed_containers()

for container in reversed(containers):
container.on_stopping()
manager.create_docker_compose_process(
containers,
"down",
).check_call()
for container in containers:
container.on_stopped()

for container in containers:
container.on_removed()


command = Command()
Expand Down
Loading

0 comments on commit 1eaf7a9

Please sign in to comment.