userver: en/testsuite/environment/control.py Source File
Loading...
Searching...
No Matches
control.py
1import dataclasses
2import getpass
3import pathlib
4import typing
5
6from testsuite.utils import yaml_util
7
8from . import service, utils
9
10CONFIG_PATH = pathlib.Path('~/.config/yasuite/env.yaml')
11DEFAULT_WORKER_ID = 'master'
12
13
14class BaseError(Exception):
15 """Base class for exceptions of this module."""
16
17
19 pass
20
21
23 pass
24
25
26@dataclasses.dataclass(frozen=True)
27class Config:
28 env_dir: pathlib.Path
29 worker_id: str
30 reuse_services: bool
31 verbose: int
32
33
35 config: Config
36 _services: dict[str, service.ScriptService]
37 _services_start_order: list[str]
38 _env: dict[str, str] | None
39 _service_factories: dict[str, typing.Callable]
40
41 def __init__(
42 self,
43 config: Config,
44 env: dict[str, str] | None = None,
45 ) -> None:
46 self.config = config
47 self._services = {}
48 self._services_start_order = []
49 self._env = env
50 self._service_factories = {}
51
52 def register_service(self, name: str, factory) -> None:
53 self._service_factories[name] = factory
54
55 def ensure_started(self, service_name: str, **kwargs) -> None:
56 if service_name not in self._services:
57 self.start_service(service_name, **kwargs)
58
59 def start_service(self, service_name: str, **kwargs) -> None:
60 if service_name in self._services:
61 raise AlreadyStarted(f'Service {service_name} is already started')
62 script_service = self._create_service(service_name, **kwargs)
63 if not (self.config.reuse_services and script_service.is_running()):
64 script_service.ensure_started(verbose=self.config.verbose)
65 self._services_start_order.append(service_name)
66 self._services[service_name] = script_service
67
68 def stop_service(self, service_name: str) -> None:
69 if service_name not in self._services:
70 self._services[service_name] = self._create_service(service_name)
71 if not self.config.reuse_services:
72 self._services[service_name].stop(verbose=self.config.verbose)
73
74 def close(self) -> None:
75 while self._services_start_order:
76 service_name = self._services_start_order.pop()
77 self.stop_service(service_name)
78 self._services.pop(service_name)
79
80 def _create_service(
81 self,
82 service_name: str,
83 **kwargs,
85 if service_name not in self._service_factories:
86 raise ServiceUnknown(f'Unknown service {service_name} requested')
87 service_class = self._service_factories[service_name]
88 return service_class(
89 service_name=service_name,
90 working_dir=self._get_working_dir_for(service_name),
91 env=self._env,
92 **kwargs,
93 )
94
95 def _get_working_dir_for(self, service_name: str) -> pathlib.Path:
96 working_dir = self.config.env_dir.joinpath(
97 'services',
98 utils.DOCKERTEST_WORKER,
99 service_name,
100 )
101 if self.config.worker_id != 'master':
102 return working_dir.joinpath('_' + self.config.worker_id)
103 return working_dir
104
105
107 def __init__(self, config: Config) -> None:
108 if config.worker_id == DEFAULT_WORKER_ID:
109 worker_suffix = '_' + config.worker_id
110 else:
111 worker_suffix = ''
112 super().__init__(
113 config=config,
114 env={'WORKER_SUFFIX': worker_suffix},
115 )
116
117
118def load_environment_config(
119 *,
120 env_dir: pathlib.Path | None = None,
121 worker_id: str = DEFAULT_WORKER_ID,
122 reuse_services: bool = False,
123 verbose: int = 0,
124) -> Config:
125 base_config = _load_config()
126 if env_dir is None:
127 if 'direcotry' in base_config:
128 env_dir = pathlib.Path(base_config['direcotry'])
129 else:
130 env_dir = pathlib.Path(f'/tmp/.yasuite-{getpass.getuser()}')
131 return Config(
132 env_dir=env_dir.expanduser().resolve(),
133 worker_id=worker_id,
134 reuse_services=reuse_services,
135 verbose=verbose,
136 )
137
138
139def _load_config():
140 config_path = CONFIG_PATH.expanduser()
141 if config_path.exists():
142 return yaml_util.load_file(config_path)
143 return {}