userver
C++ Async Framework
Toggle main menu visibility
Loading...
Searching...
No Matches
service.py
1
import
logging
2
import
os
3
import
pathlib
4
import
time
5
import
typing
6
7
from
testsuite
import
types
8
from
testsuite.utils
import
traceback
9
10
from
.
import
shell, utils
11
12
logger = logging.getLogger(__name__)
13
14
_TESTSUITE_LIB_UTILS = pathlib.Path(__file__).parent.joinpath(
15
'scripts/utils.sh'
,
16
)
17
COMMAND_START =
'start'
18
COMMAND_STOP =
'stop'
19
20
21
class
BaseError
(Exception):
22
pass
23
24
25
class
ServiceFailedToStartError
(
BaseError
):
26
pass
27
28
29
__tracebackhide__ = traceback.hide(BaseError)
30
31
32
class
ScriptService
:
33
def
__init__(
34
self,
35
*,
36
service_name: str,
37
script_path: str,
38
working_dir: str,
39
check_host: str =
'localhost'
,
40
check_ports: list[int],
41
environment: dict[str, str] |
None
=
None
,
42
prestart_hook: typing.Callable |
None
=
None
,
43
start_timeout: float = 2.0,
44
) ->
None
:
45
self.
_service_name
= service_name
46
self.
_script_path
= script_path
47
self.
_environment
= environment
48
self.
_check_host
= check_host
49
self.
_check_ports
= check_ports
50
self.
_prestart_hook
= prestart_hook
51
self.
_start_timeout
= start_timeout
52
self.
_started_mark
=
StartedMark
(working_dir)
53
54
def
ensure_started(self, *, verbose: int) ->
None
:
55
self.
_started_mark
.delete()
56
self.
stop
(verbose=0)
57
if
self.
_prestart_hook
:
58
self.
_prestart_hook
()
59
if
verbose:
60
logger.info(
'Starting %s service...'
, self.
_service_name
)
61
self.
_command
(COMMAND_START, verbose)
62
if
not
self.
_wait_for_ports
():
63
raise
ServiceFailedToStartError
(
64
f
'Service {self._service_name} failed to start within {self._start_timeout} seconds.'
65
)
66
if
verbose:
67
logger.info(
'Service %s started.'
, self.
_service_name
)
68
self.
_started_mark
.create()
69
70
def
stop(self, *, verbose: int) ->
None
:
71
self.
_started_mark
.delete()
72
if
verbose:
73
logger.info(
'Stopping %s services...'
, self.
_service_name
)
74
self.
_command
(COMMAND_STOP, verbose)
75
if
verbose:
76
logger.info(
'Service %s stopped.'
, self.
_service_name
)
77
78
def
is_running(self) -> bool:
79
if
not
self.
_started_mark
.exists():
80
return
False
81
return
all(
82
utils.test_tcp_connection(self.
_check_host
, port)
83
for
port
in
self.
_check_ports
84
)
85
86
def
_command(self, command: str, verbose: int) ->
None
:
87
env = os.environ.copy()
88
env[
'TESTSUITE_LIB_UTILS'
] = str(_TESTSUITE_LIB_UTILS)
89
if
self.
_environment
:
90
env.update(self.
_environment
)
91
args = [self.
_script_path
, command]
92
shell.execute(
93
args,
94
env=env,
95
verbose=verbose,
96
command_alias=f
'env/{self._service_name}/{command}'
,
97
)
98
99
def
_wait_for_ports(self) -> bool:
100
start_time = time.perf_counter()
101
for
port
in
self.
_check_ports
:
102
time_passed = time.perf_counter() - start_time
103
if
time_passed >= self.
_start_timeout
:
104
return
False
105
106
if
not
utils.wait_tcp_connection(
107
host=self.
_check_host
,
108
port=port,
109
timeout=self.
_start_timeout
- time_passed,
110
):
111
return
False
112
113
return
True
114
115
116
class
StartedMark
:
117
def
__init__(self, working_dir: types.PathOrStr) ->
None
:
118
self.
_path
= pathlib.Path(working_dir) /
'.started'
119
120
def
create(self) -> None:
121
self.
_path
.parent.mkdir(exist_ok=
True
, parents=
True
)
122
self.
_path
.write_text(
''
)
123
124
def
delete(self) -> None:
125
try
:
126
self.
_path
.unlink()
127
except
FileNotFoundError:
128
pass
129
130
def
exists(self) -> bool:
131
return
self.
_path
.exists()
en
testsuite
environment
service.py
Generated on
for userver by
Doxygen
1.17.0