cpbox 1.6.859__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. cpbox/__init__.py +1 -0
  2. cpbox/_assets/docker/Dockerfile.j2 +41 -0
  3. cpbox/_assets/docker/debian.sources.tuna.j2 +11 -0
  4. cpbox/_assets/docker/pip.conf.j2 +8 -0
  5. cpbox/app/appconfig.py +27 -0
  6. cpbox/app/contextdata.py +76 -0
  7. cpbox/app/contracts.py +152 -0
  8. cpbox/app/devops.py +200 -0
  9. cpbox/app/env.py +34 -0
  10. cpbox/app/eventlog.py +40 -0
  11. cpbox/app/multiuser.py +183 -0
  12. cpbox/app/rootcontext.py +40 -0
  13. cpbox/app/tools/tibaselog.py +221 -0
  14. cpbox/app/xapp.py +200 -0
  15. cpbox/clis/aliyunoss.py +84 -0
  16. cpbox/clis/mysqlcli.py +99 -0
  17. cpbox/clis/pipcli.py +40 -0
  18. cpbox/clis/rediscli.py +60 -0
  19. cpbox/tool/array.py +21 -0
  20. cpbox/tool/cache.py +133 -0
  21. cpbox/tool/cls.py +20 -0
  22. cpbox/tool/concurrent.py +215 -0
  23. cpbox/tool/crontab.py +359 -0
  24. cpbox/tool/datatypes.py +259 -0
  25. cpbox/tool/dockerutil.py +51 -0
  26. cpbox/tool/file.py +109 -0
  27. cpbox/tool/functocli.py +481 -0
  28. cpbox/tool/http.py +40 -0
  29. cpbox/tool/id.py +50 -0
  30. cpbox/tool/local_cache.py +59 -0
  31. cpbox/tool/logger.py +227 -0
  32. cpbox/tool/net.py +39 -0
  33. cpbox/tool/pkgres.py +32 -0
  34. cpbox/tool/redistool.py +69 -0
  35. cpbox/tool/serde.py +63 -0
  36. cpbox/tool/spec.py +44 -0
  37. cpbox/tool/strings.py +50 -0
  38. cpbox/tool/strjson.py +6 -0
  39. cpbox/tool/system.py +96 -0
  40. cpbox/tool/template.py +43 -0
  41. cpbox/tool/testutils.py +40 -0
  42. cpbox/tool/timeutil.py +7 -0
  43. cpbox/tool/utils.py +115 -0
  44. cpbox/xenv/compose_manager.py +119 -0
  45. cpbox/xenv/conda_manager.py +211 -0
  46. cpbox/xenv/image_manager.py +268 -0
  47. cpbox/xenv/project_app.py +24 -0
  48. cpbox/xenv/sync_manager.py +84 -0
  49. cpbox/xenv/xenv_cli.py +155 -0
  50. cpbox/xkit/agent_assets_manager.py +351 -0
  51. cpbox/xkit/artifact_manager.py +314 -0
  52. cpbox/xkit/base_tools.py +35 -0
  53. cpbox/xkit/file_select.py +93 -0
  54. cpbox/xkit/objstore_manager.py +69 -0
  55. cpbox/xkit/pybuild_manager.py +417 -0
  56. cpbox/xkit/tools/__init__.py +0 -0
  57. cpbox/xkit/tools/objstore.py +180 -0
  58. cpbox/xkit/tools/profiles.py +23 -0
  59. cpbox/xkit/xkit_cli.py +104 -0
  60. cpbox/xkit/xkit_config.py +52 -0
  61. cpbox-1.6.859.dist-info/METADATA +20 -0
  62. cpbox-1.6.859.dist-info/RECORD +65 -0
  63. cpbox-1.6.859.dist-info/WHEEL +5 -0
  64. cpbox-1.6.859.dist-info/entry_points.txt +3 -0
  65. cpbox-1.6.859.dist-info/top_level.txt +1 -0
cpbox/__init__.py ADDED
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,41 @@
1
+ FROM {% if XENV_DOCKER_BASE_IMAGE_PROXY_HOST %}{{ XENV_DOCKER_BASE_IMAGE_PROXY_HOST }}/{% endif %}continuumio/miniconda3:24.11.1-0
2
+
3
+ ARG TZ=Asia/Shanghai
4
+ ENV XENV_CONDA_ENV=xenv
5
+
6
+ WORKDIR /xmnt/app-working-root
7
+
8
+ ENV DEBIAN_FRONTEND=noninteractive
9
+ ENV TZ=${TZ}
10
+
11
+ COPY output/xenv-storage/runtime/build-assets/docker/debian.sources.tuna /etc/apt/sources.list.d/debian.sources
12
+
13
+ RUN apt-get update && apt-get install -y --no-install-recommends \
14
+ build-essential \
15
+ && rm -rf /var/lib/apt/lists/*
16
+
17
+ RUN ln -snf /usr/share/zoneinfo/${TZ} /etc/localtime \
18
+ && ln -snf /usr/share/zoneinfo/${TZ} /etc/timezone
19
+
20
+ COPY output/xenv-storage/runtime/build-assets/docker/pip.conf /root/.pip/pip.conf
21
+
22
+ COPY environment.yml /tmp/environment.yml
23
+ COPY requirements-prod.txt /tmp/requirements-prod.txt
24
+ COPY requirements-dev.txt /tmp/requirements-dev.txt
25
+ COPY pyproject.toml /tmp/pyproject.toml
26
+
27
+ RUN conda env create -n ${XENV_CONDA_ENV} -f /tmp/environment.yml \
28
+ && conda clean -afy
29
+
30
+ RUN conda run -n ${XENV_CONDA_ENV} pip install --no-cache-dir -r /tmp/requirements-prod.txt \
31
+ && conda run -n ${XENV_CONDA_ENV} pip install --no-cache-dir -r /tmp/requirements-dev.txt
32
+
33
+ RUN printf '%s\n' '#!/bin/bash' 'exec conda run -n "$XENV_CONDA_ENV" --no-capture-output "$@"' > /usr/local/bin/xenv-entrypoint \
34
+ && chmod +x /usr/local/bin/xenv-entrypoint
35
+
36
+ ENV PYTHONPATH=/xmnt/app-working-root/src
37
+ ENV PATH=/opt/conda/envs/xenv/bin:/opt/conda/condabin:${PATH}
38
+ ENV CONDA_DEFAULT_ENV=${XENV_CONDA_ENV}
39
+
40
+ ENTRYPOINT ["/usr/local/bin/xenv-entrypoint"]
41
+ CMD ["python", "--version"]
@@ -0,0 +1,11 @@
1
+ Types: deb
2
+ URIs: https://mirrors.tuna.tsinghua.edu.cn/debian
3
+ Suites: bookworm bookworm-updates
4
+ Components: main
5
+ Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
6
+
7
+ Types: deb
8
+ URIs: https://security.debian.org/debian-security
9
+ Suites: bookworm-security
10
+ Components: main
11
+ Signed-By: /usr/share/keyrings/debian-archive-keyring.gpg
@@ -0,0 +1,8 @@
1
+ [global]
2
+ {% if XENV_PIP_INDEX_HOST -%}
3
+ index=https://{{ XENV_PIP_INDEX_HOST }}/repository/pypi-all/pypi
4
+ index-url=https://{{ XENV_PIP_INDEX_HOST }}/repository/pypi-all/simple
5
+ {% else -%}
6
+ index = https://pypi.tuna.tsinghua.edu.cn/pypi
7
+ index-url = https://pypi.tuna.tsinghua.edu.cn/simple
8
+ {% endif %}
cpbox/app/appconfig.py ADDED
@@ -0,0 +1,27 @@
1
+ from cpbox.tool.utils import Singleton
2
+
3
+
4
+ class ConfigData():
5
+ __metaclass__ = Singleton
6
+
7
+ def __init__(self):
8
+ self._env = None
9
+ self._app_name = None
10
+ self._app_config = {}
11
+
12
+ def get_env(self):
13
+ return self._env
14
+
15
+ def get_app_name(self):
16
+ return self._app_name
17
+
18
+ def init(self, app_name, env = None, app_config = {}):
19
+ self._app_name = app_name
20
+ if env is not None:
21
+ self._env = env
22
+ self._app_config = app_config
23
+
24
+ def get_app_config(self):
25
+ return self._app_config
26
+
27
+ appconfig = ConfigData()
@@ -0,0 +1,76 @@
1
+ from cpbox.tool import datatypes
2
+
3
+
4
+ class EnvDefaults(datatypes.TypedValueMap):
5
+
6
+ allowed_keys = (
7
+ 'APP_ENV',
8
+ 'LOG.LEVEL',
9
+ 'LOG.DISABLE_STDOUT_LOGGER',
10
+ 'LOG.NOISE_LOGGERS',
11
+ 'LOG.NOISE_LEVEL',
12
+ 'CONDA_DEFAULT_ENV_NAME',
13
+ 'XENV_IMAGE_NAME',
14
+ 'XENV_IMAGE_PUSH_REGISTRY_HOST',
15
+ 'XENV_IMAGE_PULL_REGISTRY_HOST',
16
+ 'XENV_DOCKER_BASE_IMAGE_PROXY_HOST',
17
+ 'XENV_PIP_INDEX_HOST',
18
+ )
19
+
20
+ def __init__(self):
21
+ super().__init__({})
22
+ self._used = False
23
+
24
+ def set(self, key, value):
25
+ self[key] = value
26
+
27
+ def __setitem__(self, key, value):
28
+ self._check_writable(key)
29
+ dict.__setitem__(self, key, value)
30
+
31
+ def update(self, *args, **kwargs):
32
+ data = dict(*args, **kwargs)
33
+ for key in data:
34
+ self._check_writable(key)
35
+ for key, value in data.items():
36
+ dict.__setitem__(self, key, value)
37
+
38
+ def setdefault(self, key, default=None):
39
+ self._check_writable(key)
40
+ return dict.setdefault(self, key, default)
41
+
42
+ def pop(self, key, default=None):
43
+ self._check_mutable()
44
+ return dict.pop(self, key, default)
45
+
46
+ def popitem(self):
47
+ self._check_mutable()
48
+ return dict.popitem(self)
49
+
50
+ def clear(self):
51
+ self._check_mutable()
52
+ dict.clear(self)
53
+
54
+ def mark_used(self):
55
+ self._used = True
56
+
57
+ def _check_mutable(self):
58
+ if self._used:
59
+ raise RuntimeError('gcontext.env_defaults has already been used to initialize env_config.')
60
+
61
+ def _check_writable(self, key):
62
+ self._check_mutable()
63
+ if key not in self.allowed_keys:
64
+ raise KeyError(f'env key is not allowed in gcontext.env_defaults: {key}')
65
+
66
+
67
+ class RuntimeStore:
68
+
69
+ def __init__(self):
70
+ self._data = {}
71
+
72
+ def set(self, key, value):
73
+ self._data[key] = value
74
+
75
+ def get(self, key, default=None):
76
+ return self._data.get(key, default)
cpbox/app/contracts.py ADDED
@@ -0,0 +1,152 @@
1
+ import os
2
+ from pathlib import Path
3
+ import sys
4
+ import importlib
5
+
6
+ import dotenv
7
+
8
+ from cpbox.tool import pkgres
9
+ from cpbox.tool import datatypes
10
+ from cpbox.tool import serde
11
+
12
+
13
+ _XAPP_CONFIG_DIR = 'xapp-config'
14
+ _XAPP_STORAGE_DIR = 'xapp-storage'
15
+ _XAPP_ENV_FILE = 'xapp.env'
16
+
17
+
18
+ def _detect_start_dir():
19
+ # 启动目录判定规则:
20
+ # 1) 正常脚本方式启动(python xxx.py)时,优先使用入口脚本所在目录,后续向上查找 .env 会更符合项目预期。
21
+ # 2) 交互式场景(如 REPL / notebook / python -c)通常没有 __main__.__file__,回退到当前工作目录 cwd。
22
+ if hasattr(sys.modules['__main__'], '__file__'):
23
+ return Path(sys.argv[0]).resolve().parent
24
+ return Path.cwd()
25
+
26
+
27
+ def detect_working_root(start_dir=None):
28
+ if start_dir is None:
29
+ start_dir = _detect_start_dir()
30
+ else:
31
+ start_dir = Path(start_dir).resolve()
32
+ while True:
33
+ if (start_dir / '.env').exists():
34
+ return start_dir
35
+ if start_dir == start_dir.parent:
36
+ return None
37
+ start_dir = start_dir.parent
38
+
39
+
40
+ def is_bundle_runtime():
41
+ return bool(getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'))
42
+
43
+
44
+ def detect_bundle_root_dir():
45
+ if is_bundle_runtime():
46
+ return Path(sys._MEIPASS).resolve()
47
+ return None
48
+
49
+
50
+ def _find_package_path(package):
51
+ module = importlib.import_module(package)
52
+ package_paths = list(getattr(module, '__path__', []))
53
+ if package_paths:
54
+ return Path(package_paths[0]).resolve()
55
+ if getattr(module, '__file__', None):
56
+ return Path(module.__file__).resolve().parent
57
+ return None
58
+
59
+
60
+ def package_resource_root(package, source_rel_path, resource_package):
61
+ package_path = _find_package_path(package)
62
+ if package_path is not None:
63
+ source_tree_root = detect_working_root(package_path)
64
+ if source_tree_root is not None:
65
+ source_path = source_tree_root / source_rel_path
66
+ if source_path.exists():
67
+ return pkgres.ResourceRoot.from_dir(source_path)
68
+ return pkgres.ResourceRoot.from_package(resource_package)
69
+
70
+
71
+ def _resolve_env_path(env_key, fallback):
72
+ value = os.environ.get(env_key)
73
+ return Path(value).expanduser() if value else fallback
74
+
75
+
76
+ def system_config_root():
77
+ if sys.platform == 'win32':
78
+ return _resolve_env_path('PROGRAMDATA', Path('C:/ProgramData')) / _XAPP_CONFIG_DIR
79
+ return Path('/etc') / _XAPP_CONFIG_DIR
80
+
81
+
82
+ def user_config_home():
83
+ if sys.platform == 'win32':
84
+ return _resolve_env_path('APPDATA', Path.home() / 'AppData' / 'Roaming')
85
+ return _resolve_env_path('XDG_CONFIG_HOME', Path.home() / '.config')
86
+
87
+
88
+ def user_config_root():
89
+ return user_config_home() / _XAPP_CONFIG_DIR
90
+
91
+
92
+ def system_storage_root():
93
+ if sys.platform == 'win32':
94
+ return _resolve_env_path('PROGRAMDATA', Path('C:/ProgramData')) / _XAPP_STORAGE_DIR
95
+ return Path('/var/lib') / _XAPP_STORAGE_DIR
96
+
97
+
98
+ def user_data_home():
99
+ if sys.platform == 'win32':
100
+ return _resolve_env_path('LOCALAPPDATA', Path.home() / 'AppData' / 'Local')
101
+ return _resolve_env_path('XDG_DATA_HOME', Path.home() / '.local' / 'share')
102
+
103
+
104
+ def user_storage_root():
105
+ return user_data_home() / _XAPP_STORAGE_DIR
106
+
107
+
108
+ def xapp_default_system_config_file():
109
+ return system_config_root() / _XAPP_ENV_FILE
110
+
111
+
112
+ def xapp_default_user_config_file():
113
+ return user_config_root() / _XAPP_ENV_FILE
114
+
115
+
116
+ def load_system_user_config_file(config_file_path):
117
+ merged = {}
118
+ for config_root in (system_config_root(), user_config_root()):
119
+ path = config_root / config_file_path
120
+ if not path.exists():
121
+ continue
122
+ datatypes.merge(merged, serde.load_yaml_file(path, {}) or {})
123
+ return datatypes.TypedValueMap(merged)
124
+
125
+
126
+ def _load_dotenv_values(env_file, fallback):
127
+ return dotenv.dotenv_values(env_file) if env_file.exists() else fallback
128
+
129
+
130
+ def load_env_config_by_priority(working_root=None, env_defaults=None):
131
+ # Priority: process env > .env.local > .env > user config > system config > env_defaults > code fallbacks.
132
+ # env_defaults is a handy code-provided config source; it avoids forcing defaults into .env and stays overrideable by env files and process env.
133
+ # code fallbacks are caller-provided defaults used when reading env_config values.
134
+ # Env files are not exported to os.environ.
135
+ env_data = dict(env_defaults or {})
136
+ env_data.update(_load_dotenv_values(xapp_default_system_config_file(), {}))
137
+ env_data.update(_load_dotenv_values(xapp_default_user_config_file(), {}))
138
+ if working_root:
139
+ env_data.update(_load_dotenv_values(working_root / '.env', {}))
140
+ env_data.update(_load_dotenv_values(working_root / '.env.local', {}))
141
+ env_data = {key: value for key, value in env_data.items() if value is not None}
142
+ env_data.update(os.environ)
143
+ return datatypes.TypedValueMap(env_data)
144
+
145
+
146
+ def normalize_app_env(app_env=None):
147
+ # APP_ENV default: missing, empty, or invalid values resolve to prod.
148
+ app_env = app_env or 'prod'
149
+ app_env = str(app_env).lower()
150
+ if app_env not in ('dev', 'test', 'pre', 'prod'):
151
+ return 'prod'
152
+ return app_env
cpbox/app/devops.py ADDED
@@ -0,0 +1,200 @@
1
+ import os
2
+ import socket
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ from cpbox.app.appconfig import appconfig
7
+ from cpbox.tool import file
8
+ from cpbox.tool import functocli
9
+ from cpbox.tool import spec
10
+ from cpbox.tool import system
11
+ from cpbox.tool import template
12
+ from cpbox.tool import utils
13
+ from cpbox.app.tools import tibaselog
14
+
15
+ is_windows = os.name == 'nt'
16
+
17
+ class DevOpsAppConfig:
18
+
19
+ def __init__(self, app_name, provider):
20
+ appconfig.init(app_name, provider.get_env(), provider.get_app_config())
21
+
22
+
23
+ class DevOpsAppConfigContext:
24
+
25
+ def __init__(self, app_name, kwargs):
26
+
27
+ self.app_base_name = app_name
28
+ self.kwargs = kwargs
29
+ self.sandbox_version = kwargs.get('sandbox_version', 0)
30
+
31
+ self.env = self._get_env()
32
+ self.app_name = self._determine_app_name()
33
+
34
+ self.construct_baisc_dir()
35
+
36
+ def _determine_app_name(self):
37
+ app_name = self.app_base_name
38
+ if self.sandbox_version != 0:
39
+ app_name = '%s-sandbox-%03d' % (app_name, self.sandbox_version)
40
+ return app_name
41
+
42
+ def construct_baisc_dir(self):
43
+ default_data_dir = '/opt/data' if not is_windows else 'D:\\opt\\data'
44
+ data_dir = Path(os.environ.get('CPBOX_DATA_DIR', default_data_dir))
45
+
46
+ self.app_storage_dir = data_dir / self.app_name
47
+
48
+ self.app_persistent_storage_dir = self.app_storage_dir / 'persistent'
49
+ self.app_runtime_storage_dir = self.app_storage_dir / 'runtime'
50
+ self.app_logs_dir = self.app_runtime_storage_dir / 'logs'
51
+
52
+ def is_dev(self):
53
+ return self.env == 'dev' or self.env == 'test'
54
+
55
+ def _get_env(self):
56
+ env = 'dev'
57
+ if 'PUPPY_ENV' in os.environ:
58
+ env = os.environ['PUPPY_ENV']
59
+ if 'CPBOX_ENV' in os.environ:
60
+ env = os.environ['CPBOX_ENV']
61
+ if 'env' in self.kwargs:
62
+ env = self.kwargs['env']
63
+ return env
64
+
65
+ def get_sandbox_code_dir(self):
66
+ sandbox_code_dir = None
67
+ base_dir = self.app_persistent_storage_dir
68
+ if self.sandbox_version != 0:
69
+ sandbox_code_dir = '%s/sandbox-code-%03d' % (base_dir, self.sandbox_version)
70
+ sandbox_code_dir = Path(sandbox_code_dir)
71
+ return sandbox_code_dir
72
+
73
+ def get_root_dir(self):
74
+ if hasattr(sys.modules['__main__'], '__file__'):
75
+ root_dir = Path(sys.argv[0]).resolve().parent
76
+ return root_dir
77
+ else:
78
+ root_dir = Path.cwd()
79
+ return root_dir
80
+
81
+ def ensure_dir_and_write_permission(self):
82
+ file.ensure_dir(self.app_persistent_storage_dir)
83
+ file.ensure_dir(self.app_runtime_storage_dir)
84
+ file.ensure_dir(self.app_logs_dir)
85
+ file.ensure_dir(self.app_logs_dir / 'syslog')
86
+
87
+
88
+ class DevOpsApp(functocli.SimpleApp):
89
+
90
+ def __init__(self, app_name, **kwargs):
91
+ functocli.SimpleApp.__init__(self)
92
+ self.config_context = DevOpsAppConfigContext(app_name, kwargs)
93
+ self.env = self.config_context.env
94
+
95
+ self.construct_baisc_dir()
96
+
97
+ self.logger = self._make_logger(app_name, kwargs)
98
+
99
+ self.file_lock = None
100
+
101
+ def run_cmd_ret(self, cmd, log=True):
102
+ return self.run_cmd(cmd, log=log)[1]
103
+
104
+ def run_cmd(self, cmd, log=True):
105
+ if log:
106
+ self.logger.info('run_cmd: %s', cmd)
107
+ return system.run_cmd(cmd)
108
+
109
+ def shell_run(self, cmd, keep_pipeline=True, exit_on_error=True, dry_run=False, log=True):
110
+ if log:
111
+ self.logger.info('shell_run: %s', cmd)
112
+ if dry_run:
113
+ return 0
114
+ return system.shell_run(cmd, keep_pipeline=keep_pipeline, exit_on_error=exit_on_error)
115
+
116
+ def _make_logger(self, app_name, kwargs):
117
+ tibaselog.log_manager.add_file_logger(self.app_logs_dir, f'{app_name}.log')
118
+ import logging
119
+ return logging.getLogger(app_name)
120
+
121
+ def construct_baisc_dir(self):
122
+
123
+ context = self.config_context
124
+ context.construct_baisc_dir()
125
+ context.ensure_dir_and_write_permission()
126
+
127
+ hostname = socket.gethostname()
128
+ self.hostname_fqdn = hostname
129
+ self.hostname_short = hostname.split('.', 1)[0]
130
+
131
+ self.root_dir = context.get_sandbox_code_dir() if context.sandbox_version else context.get_root_dir()
132
+ self._roles_dir = self.root_dir / 'roles'
133
+
134
+ app_root_dir = self._roles_dir / context.app_base_name
135
+
136
+ self.app_root_dir = app_root_dir
137
+ self.app_config_dir = app_root_dir / 'config'
138
+ self.app_templates_dir = app_root_dir / 'templates'
139
+ self.app_scripts_dir = app_root_dir / 'scripts'
140
+
141
+ self.app_storage_dir = context.app_storage_dir
142
+ self.app_persistent_storage_dir = context.app_persistent_storage_dir
143
+ self.app_runtime_storage_dir = context.app_runtime_storage_dir
144
+ self.app_logs_dir = context.app_logs_dir
145
+
146
+ def remove_container(self, name, force=False, dry_run=False):
147
+ cmd = 'docker rm %s' % (name)
148
+ if force:
149
+ cmd = 'docker rm -f %s' % (name)
150
+ return self.shell_run(cmd, exit_on_error=False, dry_run=dry_run)
151
+
152
+ def container_is_running(self, name):
153
+ cmd = 'docker ps | grep %s' % (name)
154
+ return self.run_cmd(cmd, log=False)[0] == 0
155
+
156
+ def stop_container(self, name, timeout=300, dry_run=False):
157
+ cmd = 'docker stop --time %d %s' % (timeout, name)
158
+ return self.shell_run(cmd, exit_on_error=False, dry_run=dry_run)
159
+
160
+ def _check_lock(self):
161
+ filepath = self.app_runtime_storage_dir / 'locks' / file.compute_lock_filepath(sys.argv)
162
+ file_lock = file.obtain_lock(filepath)
163
+ if file_lock is None:
164
+ pid = 0
165
+ with open(filepath, 'r') as f:
166
+ pid = f.read()
167
+ self.logger.warning('lock file exists, pid: %s => %s', pid, filepath)
168
+ sys.exit(1)
169
+ else:
170
+ self.file_lock = file_lock
171
+
172
+ def template_to(self, template_filename, dst, payload, app_name=None):
173
+ template_payload = {'payload': payload}
174
+ src = self.app_templates_dir_for(app_name) / template_filename
175
+ template.render_to_file(src, template_payload, dst)
176
+ self.logger.info('template_to: %s => %s', src, dst)
177
+
178
+ def read_app_config(self, config_filebasename, app_name=None):
179
+ if '.yml' not in config_filebasename:
180
+ config_filebasename = config_filebasename + '.yml'
181
+ data = utils.load_yaml(self.app_config_dir_for(app_name) / config_filebasename, {})
182
+ return data
183
+
184
+ def app_templates_dir_for(self, app_name=None):
185
+ if app_name is None:
186
+ return self.app_templates_dir
187
+ return self._roles_dir / app_name / 'templates'
188
+
189
+ def app_config_dir_for(self, app_name=None):
190
+ if app_name is None:
191
+ return self.app_config_dir
192
+ return self._roles_dir / app_name / 'config'
193
+
194
+ @functocli.keep_method_for_inheritance
195
+ def check_health(self):
196
+ print('%s is good' % (sys.argv[0]))
197
+
198
+ @staticmethod
199
+ def run_app(app, log_level='info', default_method=None, common_args_option=None):
200
+ functocli.run_app(app, default_method=default_method)
cpbox/app/env.py ADDED
@@ -0,0 +1,34 @@
1
+ from cpbox.tool import cache
2
+ from cpbox.tool import utils
3
+
4
+
5
+ class EnvTool(object):
6
+
7
+ def __init__(self, app):
8
+ self.app = app
9
+ fn = self.app.root_dir / '.box-config.yml'
10
+ self.box_config = utils.load_yaml(fn, {})
11
+
12
+ def docker_url_by_env(self, image_name):
13
+ group_names = self.app.get_group_names()
14
+ if 'nexus-genesis' in group_names:
15
+ return image_name
16
+ if 'firelighter-server' in group_names:
17
+ return self.docker_genesis_url(image_name)
18
+ else:
19
+ return self.docker_url(image_name)
20
+
21
+ def docker_url(self, image_name=''):
22
+ return self.box_config.get('docker_url_pre') + image_name
23
+
24
+ def docker_publish_url(self, image_name=''):
25
+ return self.box_config.get('docker_publish_url_pre') + image_name
26
+
27
+ def docker_genesis_url(self, image_name=''):
28
+ return self.box_config.get('docker_genesis_url_pre') + image_name
29
+
30
+ def setup_redis_for_cache(self):
31
+ config = self.box_config[self.app.env]
32
+ redis = config['redis']
33
+ redis_url = 'redis://:%s@%s:%s' % (redis['auth'], redis['host'], redis['port'])
34
+ cache.set_redis_url(redis_url)
cpbox/app/eventlog.py ADDED
@@ -0,0 +1,40 @@
1
+ import json
2
+ import logging
3
+ import time
4
+ from cpbox.tool import timeutil
5
+
6
+ from cpbox.app.appconfig import appconfig
7
+
8
+ event_logger = logging.getLogger('event-log')
9
+
10
+
11
+ def add_event_log(event_key, payload):
12
+ payload['env'] = appconfig.get_env()
13
+ msg = 'cp %s %s' % (event_key, json.dumps(payload))
14
+ event_logger.info(msg)
15
+
16
+ def add_app_monitor_log(key, value, time=None):
17
+ time = time if time else timeutil.local_now_iso8601_str()
18
+ data = {
19
+ 'event_key': key,
20
+ 'event_value': value,
21
+ 'time': time,
22
+ }
23
+ payload = {}
24
+ payload['env'] = appconfig.get_env()
25
+ payload['log'] = json.dumps(data)
26
+ msg = 'app-monitor monitor-log %s' % (json.dumps(payload))
27
+ event_logger.info(msg)
28
+
29
+
30
+ def log_func_call(func, *args, **kwargs):
31
+ def timed(*args, **kw):
32
+ start = time.time() * 1000
33
+ result = func(*args, **kw)
34
+ payload = {}
35
+ payload['name'] = func.__name__
36
+ payload['rt'] = time.time() * 1000 - start
37
+ add_event_log('func-call', payload)
38
+ return result
39
+
40
+ return timed