olas-operate-middleware 0.1.0rc59__py3-none-any.whl → 0.13.2__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.
- olas_operate_middleware-0.13.2.dist-info/METADATA +75 -0
- olas_operate_middleware-0.13.2.dist-info/RECORD +101 -0
- {olas_operate_middleware-0.1.0rc59.dist-info → olas_operate_middleware-0.13.2.dist-info}/WHEEL +1 -1
- operate/__init__.py +17 -0
- operate/account/user.py +35 -9
- operate/bridge/bridge_manager.py +470 -0
- operate/bridge/providers/lifi_provider.py +377 -0
- operate/bridge/providers/native_bridge_provider.py +677 -0
- operate/bridge/providers/provider.py +469 -0
- operate/bridge/providers/relay_provider.py +457 -0
- operate/cli.py +1565 -417
- operate/constants.py +60 -12
- operate/data/README.md +19 -0
- operate/data/contracts/{service_staking_token → dual_staking_token}/__init__.py +2 -2
- operate/data/contracts/dual_staking_token/build/DualStakingToken.json +443 -0
- operate/data/contracts/dual_staking_token/contract.py +132 -0
- operate/data/contracts/dual_staking_token/contract.yaml +23 -0
- operate/{ledger/base.py → data/contracts/foreign_omnibridge/__init__.py} +2 -19
- operate/data/contracts/foreign_omnibridge/build/ForeignOmnibridge.json +1372 -0
- operate/data/contracts/foreign_omnibridge/contract.py +130 -0
- operate/data/contracts/foreign_omnibridge/contract.yaml +23 -0
- operate/{ledger/solana.py → data/contracts/home_omnibridge/__init__.py} +2 -20
- operate/data/contracts/home_omnibridge/build/HomeOmnibridge.json +1421 -0
- operate/data/contracts/home_omnibridge/contract.py +80 -0
- operate/data/contracts/home_omnibridge/contract.yaml +23 -0
- operate/data/contracts/l1_standard_bridge/__init__.py +20 -0
- operate/data/contracts/l1_standard_bridge/build/L1StandardBridge.json +831 -0
- operate/data/contracts/l1_standard_bridge/contract.py +158 -0
- operate/data/contracts/l1_standard_bridge/contract.yaml +23 -0
- operate/data/contracts/l2_standard_bridge/__init__.py +20 -0
- operate/data/contracts/l2_standard_bridge/build/L2StandardBridge.json +626 -0
- operate/data/contracts/l2_standard_bridge/contract.py +130 -0
- operate/data/contracts/l2_standard_bridge/contract.yaml +23 -0
- operate/data/contracts/mech_activity/__init__.py +20 -0
- operate/data/contracts/mech_activity/build/MechActivity.json +111 -0
- operate/data/contracts/mech_activity/contract.py +44 -0
- operate/data/contracts/mech_activity/contract.yaml +23 -0
- operate/data/contracts/optimism_mintable_erc20/__init__.py +20 -0
- operate/data/contracts/optimism_mintable_erc20/build/OptimismMintableERC20.json +491 -0
- operate/data/contracts/optimism_mintable_erc20/contract.py +45 -0
- operate/data/contracts/optimism_mintable_erc20/contract.yaml +23 -0
- operate/data/contracts/recovery_module/__init__.py +20 -0
- operate/data/contracts/recovery_module/build/RecoveryModule.json +811 -0
- operate/data/contracts/recovery_module/contract.py +61 -0
- operate/data/contracts/recovery_module/contract.yaml +23 -0
- operate/data/contracts/requester_activity_checker/__init__.py +20 -0
- operate/data/contracts/requester_activity_checker/build/RequesterActivityChecker.json +111 -0
- operate/data/contracts/requester_activity_checker/contract.py +33 -0
- operate/data/contracts/requester_activity_checker/contract.yaml +23 -0
- operate/data/contracts/staking_token/__init__.py +20 -0
- operate/data/contracts/staking_token/build/StakingToken.json +1336 -0
- operate/data/contracts/{service_staking_token → staking_token}/contract.py +27 -13
- operate/data/contracts/staking_token/contract.yaml +23 -0
- operate/data/contracts/uniswap_v2_erc20/contract.yaml +3 -1
- operate/data/contracts/uniswap_v2_erc20/tests/__init__.py +20 -0
- operate/data/contracts/uniswap_v2_erc20/tests/test_contract.py +363 -0
- operate/keys.py +118 -33
- operate/ledger/__init__.py +159 -56
- operate/ledger/profiles.py +321 -18
- operate/migration.py +555 -0
- operate/{http → operate_http}/__init__.py +3 -2
- operate/{http → operate_http}/exceptions.py +6 -4
- operate/operate_types.py +544 -0
- operate/pearl.py +13 -1
- operate/quickstart/analyse_logs.py +118 -0
- operate/quickstart/claim_staking_rewards.py +104 -0
- operate/quickstart/reset_configs.py +106 -0
- operate/quickstart/reset_password.py +70 -0
- operate/quickstart/reset_staking.py +145 -0
- operate/quickstart/run_service.py +726 -0
- operate/quickstart/stop_service.py +72 -0
- operate/quickstart/terminate_on_chain_service.py +83 -0
- operate/quickstart/utils.py +298 -0
- operate/resource.py +62 -3
- operate/services/agent_runner.py +202 -0
- operate/services/deployment_runner.py +868 -0
- operate/services/funding_manager.py +929 -0
- operate/services/health_checker.py +280 -0
- operate/services/manage.py +2356 -620
- operate/services/protocol.py +1246 -340
- operate/services/service.py +756 -391
- operate/services/utils/mech.py +103 -0
- operate/services/utils/tendermint.py +86 -12
- operate/settings.py +70 -0
- operate/utils/__init__.py +135 -0
- operate/utils/gnosis.py +407 -80
- operate/utils/single_instance.py +226 -0
- operate/utils/ssl.py +133 -0
- operate/wallet/master.py +708 -123
- operate/wallet/wallet_recovery_manager.py +507 -0
- olas_operate_middleware-0.1.0rc59.dist-info/METADATA +0 -304
- olas_operate_middleware-0.1.0rc59.dist-info/RECORD +0 -41
- operate/data/contracts/service_staking_token/build/ServiceStakingToken.json +0 -1273
- operate/data/contracts/service_staking_token/contract.yaml +0 -23
- operate/ledger/ethereum.py +0 -48
- operate/types.py +0 -260
- {olas_operate_middleware-0.1.0rc59.dist-info → olas_operate_middleware-0.13.2.dist-info}/entry_points.txt +0 -0
- {olas_operate_middleware-0.1.0rc59.dist-info → olas_operate_middleware-0.13.2.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,868 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# ------------------------------------------------------------------------------
|
|
4
|
+
#
|
|
5
|
+
# Copyright 2024 Valory AG
|
|
6
|
+
#
|
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
# you may not use this file except in compliance with the License.
|
|
9
|
+
# You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
# See the License for the specific language governing permissions and
|
|
17
|
+
# limitations under the License.
|
|
18
|
+
#
|
|
19
|
+
# ------------------------------------------------------------------------------
|
|
20
|
+
"""Source code to run and stop deployments created."""
|
|
21
|
+
import ctypes
|
|
22
|
+
import json
|
|
23
|
+
import multiprocessing
|
|
24
|
+
import os
|
|
25
|
+
import platform
|
|
26
|
+
import shutil # nosec
|
|
27
|
+
import subprocess # nosec
|
|
28
|
+
import sys # nosec
|
|
29
|
+
import time
|
|
30
|
+
import typing as t
|
|
31
|
+
from abc import ABC, ABCMeta, abstractmethod
|
|
32
|
+
from contextlib import suppress
|
|
33
|
+
from enum import Enum
|
|
34
|
+
from io import TextIOWrapper
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
from traceback import print_exc
|
|
37
|
+
from typing import Any, Dict, List, Type
|
|
38
|
+
from venv import main as venv_cli
|
|
39
|
+
|
|
40
|
+
import psutil
|
|
41
|
+
import requests
|
|
42
|
+
from aea.__version__ import __version__ as aea_version
|
|
43
|
+
from aea.helpers.logging import setup_logger
|
|
44
|
+
from autonomy.__version__ import __version__ as autonomy_version
|
|
45
|
+
|
|
46
|
+
from operate import constants
|
|
47
|
+
|
|
48
|
+
from .agent_runner import get_agent_runner_path
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class AbstractDeploymentRunner(ABC):
|
|
52
|
+
"""Abstract deployment runner."""
|
|
53
|
+
|
|
54
|
+
def __init__(self, work_directory: Path) -> None:
|
|
55
|
+
"""Init the deployment runner."""
|
|
56
|
+
self._work_directory = work_directory
|
|
57
|
+
|
|
58
|
+
@abstractmethod
|
|
59
|
+
def start(self, password: str) -> None:
|
|
60
|
+
"""Start the deployment."""
|
|
61
|
+
|
|
62
|
+
@abstractmethod
|
|
63
|
+
def stop(self) -> None:
|
|
64
|
+
"""Stop the deployment."""
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _kill_process(pid: int) -> None:
|
|
68
|
+
"""Kill process."""
|
|
69
|
+
while True:
|
|
70
|
+
if not psutil.pid_exists(pid=pid):
|
|
71
|
+
return
|
|
72
|
+
process = psutil.Process(pid=pid)
|
|
73
|
+
if process.status() in (
|
|
74
|
+
psutil.STATUS_DEAD,
|
|
75
|
+
psutil.STATUS_ZOMBIE,
|
|
76
|
+
):
|
|
77
|
+
return
|
|
78
|
+
try:
|
|
79
|
+
process.kill()
|
|
80
|
+
except OSError:
|
|
81
|
+
return
|
|
82
|
+
except psutil.AccessDenied:
|
|
83
|
+
return
|
|
84
|
+
time.sleep(1)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def kill_process(pid: int) -> None:
|
|
88
|
+
"""Kill the process and all children first."""
|
|
89
|
+
if not psutil.pid_exists(pid=pid):
|
|
90
|
+
return
|
|
91
|
+
current_process = psutil.Process(pid=pid)
|
|
92
|
+
children = list(reversed(current_process.children(recursive=True)))
|
|
93
|
+
for child in children:
|
|
94
|
+
_kill_process(child.pid)
|
|
95
|
+
_kill_process(child.pid)
|
|
96
|
+
_kill_process(pid)
|
|
97
|
+
_kill_process(pid)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
|
|
101
|
+
"""Base deployment with aea support."""
|
|
102
|
+
|
|
103
|
+
TM_CONTROL_URL = constants.TM_CONTROL_URL
|
|
104
|
+
SLEEP_BEFORE_TM_KILL = 2 # seconds
|
|
105
|
+
START_TRIES = constants.DEPLOYMENT_START_TRIES_NUM
|
|
106
|
+
logger = setup_logger(name="operate.base_deployment_runner")
|
|
107
|
+
|
|
108
|
+
def __init__(self, work_directory: Path, is_aea: bool) -> None:
|
|
109
|
+
"""Initialize the deployment runner."""
|
|
110
|
+
super().__init__(work_directory)
|
|
111
|
+
self._is_aea = is_aea
|
|
112
|
+
|
|
113
|
+
def _open_agent_runner_log_file(self) -> TextIOWrapper:
|
|
114
|
+
"""Open agent_runner.log file."""
|
|
115
|
+
return (self._get_operate_dir() / "agent_runner.log").open("w+")
|
|
116
|
+
|
|
117
|
+
def _open_tendermint_log_file(self) -> TextIOWrapper:
|
|
118
|
+
"""Open tm.log file."""
|
|
119
|
+
return (self._get_operate_dir() / "tm.log").open("w+")
|
|
120
|
+
|
|
121
|
+
def _get_operate_dir(self) -> Path:
|
|
122
|
+
"""Get .operate dir."""
|
|
123
|
+
return Path(self._work_directory).parent.parent.parent
|
|
124
|
+
|
|
125
|
+
def _run_aea_command(self, *args: str, cwd: Path) -> Any:
|
|
126
|
+
"""Run aea command."""
|
|
127
|
+
no_password_args = []
|
|
128
|
+
for i, arg in enumerate(args):
|
|
129
|
+
if i > 0 and args[i - 1] == "--password":
|
|
130
|
+
no_password_args.append("******")
|
|
131
|
+
elif arg.startswith("--password="):
|
|
132
|
+
no_password_args.append("--password=******")
|
|
133
|
+
else:
|
|
134
|
+
no_password_args.append(arg)
|
|
135
|
+
|
|
136
|
+
self.logger.info(
|
|
137
|
+
f"Running aea command: {' '.join(no_password_args)} at {str(cwd)}"
|
|
138
|
+
)
|
|
139
|
+
p = multiprocessing.Process(
|
|
140
|
+
target=self.__class__._call_aea_command, # pylint: disable=protected-access
|
|
141
|
+
args=(cwd, args),
|
|
142
|
+
)
|
|
143
|
+
p.start()
|
|
144
|
+
p.join()
|
|
145
|
+
if p.exitcode != 0:
|
|
146
|
+
raise RuntimeError(
|
|
147
|
+
f"aea command `{' '.join(no_password_args)}` execution failed with exit code: {p.exitcode}"
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
@staticmethod
|
|
151
|
+
def _call_aea_command(cwd: str | Path, args: List[str]) -> None:
|
|
152
|
+
try:
|
|
153
|
+
import os # pylint: disable=redefined-outer-name,reimported,import-outside-toplevel
|
|
154
|
+
|
|
155
|
+
os.chdir(cwd)
|
|
156
|
+
# pylint: disable-next=import-outside-toplevel
|
|
157
|
+
from aea.cli.core import cli as call_aea
|
|
158
|
+
|
|
159
|
+
call_aea( # pylint: disable=unexpected-keyword-arg, no-value-for-parameter
|
|
160
|
+
args, standalone_mode=False
|
|
161
|
+
)
|
|
162
|
+
except Exception:
|
|
163
|
+
print(f"Error on calling aea command: {args}")
|
|
164
|
+
print_exc()
|
|
165
|
+
raise
|
|
166
|
+
|
|
167
|
+
def _run_cmd(self, args: t.List[str], cwd: t.Optional[Path] = None) -> None:
|
|
168
|
+
"""Run command in a subprocess."""
|
|
169
|
+
self.logger.info(f"Running: {' '.join(args)}")
|
|
170
|
+
self.logger.info(f"Working dir: {os.getcwd()}")
|
|
171
|
+
result = subprocess.run( # pylint: disable=subprocess-run-check # nosec
|
|
172
|
+
args=args,
|
|
173
|
+
cwd=cwd,
|
|
174
|
+
stdout=subprocess.PIPE,
|
|
175
|
+
stderr=subprocess.PIPE,
|
|
176
|
+
)
|
|
177
|
+
if result.returncode != 0:
|
|
178
|
+
raise RuntimeError(
|
|
179
|
+
f"Error running: {args} @ {cwd}\n{result.stderr.decode()}"
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
def _prepare_agent_env(self) -> Any:
|
|
183
|
+
"""Prepare agent env, add keys, run aea commands."""
|
|
184
|
+
working_dir = self._work_directory
|
|
185
|
+
env = json.loads((working_dir / "agent.json").read_text(encoding="utf-8"))
|
|
186
|
+
|
|
187
|
+
# TODO: Dynamic port allocation, backport to service builder
|
|
188
|
+
env["PYTHONUTF8"] = "1"
|
|
189
|
+
for var in env:
|
|
190
|
+
# Fix tendermint connection params
|
|
191
|
+
if var.endswith("MODELS_PARAMS_ARGS_TENDERMINT_COM_URL"):
|
|
192
|
+
env[var] = self.TM_CONTROL_URL
|
|
193
|
+
|
|
194
|
+
if var.endswith("MODELS_PARAMS_ARGS_TENDERMINT_URL"):
|
|
195
|
+
env[var] = "http://localhost:26657"
|
|
196
|
+
|
|
197
|
+
if var.endswith("MODELS_PARAMS_ARGS_TENDERMINT_P2P_URL"):
|
|
198
|
+
env[var] = "localhost:26656"
|
|
199
|
+
|
|
200
|
+
(working_dir / "agent.json").write_text(
|
|
201
|
+
json.dumps(env, indent=4),
|
|
202
|
+
encoding="utf-8",
|
|
203
|
+
)
|
|
204
|
+
return env
|
|
205
|
+
|
|
206
|
+
def _setup_agent(self, password: str) -> None:
|
|
207
|
+
"""Setup agent with retries for network operations."""
|
|
208
|
+
max_attempts = 10
|
|
209
|
+
for attempt in range(1, max_attempts + 1):
|
|
210
|
+
try:
|
|
211
|
+
working_dir = self._work_directory
|
|
212
|
+
env = self._prepare_agent_env()
|
|
213
|
+
|
|
214
|
+
# Clear agent directory before each attempt to avoid partial state
|
|
215
|
+
agent_alias_name = "agent"
|
|
216
|
+
agent_dir_full_path = Path(working_dir) / agent_alias_name
|
|
217
|
+
if agent_dir_full_path.exists():
|
|
218
|
+
with suppress(Exception):
|
|
219
|
+
shutil.rmtree(agent_dir_full_path, ignore_errors=True)
|
|
220
|
+
|
|
221
|
+
self._run_aea_command(
|
|
222
|
+
"init",
|
|
223
|
+
"--reset",
|
|
224
|
+
"--author",
|
|
225
|
+
"valory",
|
|
226
|
+
"--remote",
|
|
227
|
+
"--ipfs",
|
|
228
|
+
"--ipfs-node",
|
|
229
|
+
"/dns/registry.autonolas.tech/tcp/443/https",
|
|
230
|
+
cwd=working_dir,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
self._run_aea_command(
|
|
234
|
+
"-s",
|
|
235
|
+
"fetch",
|
|
236
|
+
env["AEA_AGENT"],
|
|
237
|
+
"--alias",
|
|
238
|
+
agent_alias_name,
|
|
239
|
+
cwd=working_dir,
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
# Add keys
|
|
243
|
+
shutil.copy(
|
|
244
|
+
working_dir / "ethereum_private_key.txt",
|
|
245
|
+
working_dir / "agent" / "ethereum_private_key.txt",
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
self._run_aea_command(
|
|
249
|
+
"-s",
|
|
250
|
+
"add-key",
|
|
251
|
+
"--password",
|
|
252
|
+
password,
|
|
253
|
+
"ethereum",
|
|
254
|
+
cwd=working_dir / "agent",
|
|
255
|
+
)
|
|
256
|
+
self._run_aea_command(
|
|
257
|
+
"-s",
|
|
258
|
+
"add-key",
|
|
259
|
+
"--password",
|
|
260
|
+
password,
|
|
261
|
+
"ethereum",
|
|
262
|
+
"--connection",
|
|
263
|
+
cwd=working_dir / "agent",
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
self._run_aea_command(
|
|
267
|
+
"-s",
|
|
268
|
+
"issue-certificates",
|
|
269
|
+
"--password",
|
|
270
|
+
password,
|
|
271
|
+
cwd=working_dir / "agent",
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# Success - break out of retry loop
|
|
275
|
+
self.logger.info(
|
|
276
|
+
f"Agent setup completed successfully on attempt {attempt}"
|
|
277
|
+
)
|
|
278
|
+
break
|
|
279
|
+
|
|
280
|
+
except Exception as e: # pylint: disable=broad-except
|
|
281
|
+
self.logger.warning(
|
|
282
|
+
f"Agent setup attempt {attempt}/{max_attempts} failed: {e}"
|
|
283
|
+
)
|
|
284
|
+
if attempt < max_attempts:
|
|
285
|
+
sleep_time = attempt * 5
|
|
286
|
+
self.logger.info(f"Retrying agent setup in {sleep_time} seconds...")
|
|
287
|
+
time.sleep(sleep_time)
|
|
288
|
+
else:
|
|
289
|
+
self.logger.error(f"All {max_attempts} agent setup attempts failed")
|
|
290
|
+
raise
|
|
291
|
+
|
|
292
|
+
def start(self, password: str) -> None:
|
|
293
|
+
"""Start the deployment with retries."""
|
|
294
|
+
for _ in range(self.START_TRIES):
|
|
295
|
+
try:
|
|
296
|
+
self._start(password=password)
|
|
297
|
+
return
|
|
298
|
+
except Exception as e: # pylint: disable=broad-except
|
|
299
|
+
self.logger.exception(f"Error on starting deployment: {e}")
|
|
300
|
+
raise RuntimeError(
|
|
301
|
+
f"Failed to start the deployment after {self.START_TRIES} attempts! Check logs"
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
def _start(self, password: str) -> None:
|
|
305
|
+
"""Start the deployment."""
|
|
306
|
+
self._setup_agent(password=password)
|
|
307
|
+
if self._is_aea:
|
|
308
|
+
self._start_tendermint()
|
|
309
|
+
|
|
310
|
+
self._start_agent(password=password)
|
|
311
|
+
|
|
312
|
+
def stop(self) -> None:
|
|
313
|
+
"""Stop the deployment."""
|
|
314
|
+
self._stop_agent()
|
|
315
|
+
if self._is_aea:
|
|
316
|
+
self._stop_tendermint()
|
|
317
|
+
|
|
318
|
+
def _stop_agent(self) -> None:
|
|
319
|
+
"""Start process."""
|
|
320
|
+
pid = self._work_directory / "agent.pid"
|
|
321
|
+
if not pid.exists():
|
|
322
|
+
return
|
|
323
|
+
kill_process(int(pid.read_text(encoding="utf-8")))
|
|
324
|
+
|
|
325
|
+
def _get_tm_exit_url(self) -> str:
|
|
326
|
+
return f"{self.TM_CONTROL_URL}/exit"
|
|
327
|
+
|
|
328
|
+
def _stop_tendermint(self) -> None:
|
|
329
|
+
"""Stop tendermint process."""
|
|
330
|
+
try:
|
|
331
|
+
requests.get(self._get_tm_exit_url(), timeout=(1, 10))
|
|
332
|
+
time.sleep(self.SLEEP_BEFORE_TM_KILL)
|
|
333
|
+
except requests.ConnectionError:
|
|
334
|
+
self.logger.error(
|
|
335
|
+
f"No Tendermint process listening on {self._get_tm_exit_url()}."
|
|
336
|
+
)
|
|
337
|
+
except Exception: # pylint: disable=broad-except
|
|
338
|
+
self.logger.exception("Exception on tendermint stop!")
|
|
339
|
+
|
|
340
|
+
pid = self._work_directory / "tendermint.pid"
|
|
341
|
+
if not pid.exists():
|
|
342
|
+
return
|
|
343
|
+
kill_process(int(pid.read_text(encoding="utf-8")))
|
|
344
|
+
|
|
345
|
+
@abstractmethod
|
|
346
|
+
def _start_tendermint(self) -> None:
|
|
347
|
+
"""Start tendermint process."""
|
|
348
|
+
|
|
349
|
+
@abstractmethod
|
|
350
|
+
def _start_agent(self, password: str) -> None:
|
|
351
|
+
"""Start aea process."""
|
|
352
|
+
|
|
353
|
+
@property
|
|
354
|
+
@abstractmethod
|
|
355
|
+
def _agent_runner_bin(self) -> str:
|
|
356
|
+
"""Return aea_bin path."""
|
|
357
|
+
raise NotImplementedError
|
|
358
|
+
|
|
359
|
+
def get_agent_start_args(self, password: str) -> List[str]:
|
|
360
|
+
"""Return agent start arguments."""
|
|
361
|
+
return (
|
|
362
|
+
[self._agent_runner_bin]
|
|
363
|
+
+ (
|
|
364
|
+
[
|
|
365
|
+
"-s",
|
|
366
|
+
"run",
|
|
367
|
+
]
|
|
368
|
+
if self._is_aea
|
|
369
|
+
else []
|
|
370
|
+
)
|
|
371
|
+
+ [
|
|
372
|
+
"--password",
|
|
373
|
+
password,
|
|
374
|
+
]
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
class PyInstallerHostDeploymentRunner(BaseDeploymentRunner):
|
|
379
|
+
"""Deployment runner within pyinstaller env."""
|
|
380
|
+
|
|
381
|
+
@property
|
|
382
|
+
def _agent_runner_bin(self) -> str:
|
|
383
|
+
"""Return aea_bin path."""
|
|
384
|
+
service_dir = self._work_directory.parent
|
|
385
|
+
agent_runner_bin = get_agent_runner_path(service_dir=service_dir)
|
|
386
|
+
return str(agent_runner_bin)
|
|
387
|
+
|
|
388
|
+
@property
|
|
389
|
+
def _tendermint_bin(self) -> str:
|
|
390
|
+
"""Return tendermint path."""
|
|
391
|
+
return str(Path(os.path.dirname(sys.executable)) / "tendermint_bin") # type: ignore # pylint: disable=protected-access
|
|
392
|
+
|
|
393
|
+
def _start_agent(self, password: str) -> None:
|
|
394
|
+
"""Start agent process."""
|
|
395
|
+
working_dir = self._work_directory
|
|
396
|
+
env = json.loads((working_dir / "agent.json").read_text(encoding="utf-8"))
|
|
397
|
+
env["PYTHONUTF8"] = "1"
|
|
398
|
+
env["PYTHONIOENCODING"] = "utf8"
|
|
399
|
+
env = {**os.environ, **env}
|
|
400
|
+
|
|
401
|
+
process = self._start_agent_process(
|
|
402
|
+
env=env, working_dir=working_dir, password=password
|
|
403
|
+
)
|
|
404
|
+
(working_dir / "agent.pid").write_text(
|
|
405
|
+
data=str(process.pid),
|
|
406
|
+
encoding="utf-8",
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
def _start_agent_process(
|
|
410
|
+
self, env: Dict, working_dir: Path, password: str
|
|
411
|
+
) -> subprocess.Popen:
|
|
412
|
+
"""Start agent process."""
|
|
413
|
+
raise NotImplementedError
|
|
414
|
+
|
|
415
|
+
def _start_tendermint(self) -> None:
|
|
416
|
+
"""Start tendermint process."""
|
|
417
|
+
working_dir = self._work_directory
|
|
418
|
+
env = json.loads((working_dir / "tendermint.json").read_text(encoding="utf-8"))
|
|
419
|
+
env["PYTHONUTF8"] = "1"
|
|
420
|
+
env["PYTHONIOENCODING"] = "utf8"
|
|
421
|
+
|
|
422
|
+
env = {
|
|
423
|
+
**os.environ,
|
|
424
|
+
**env,
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
process = self._start_tendermint_process(env=env, working_dir=working_dir)
|
|
428
|
+
|
|
429
|
+
(working_dir / "tendermint.pid").write_text(
|
|
430
|
+
data=str(process.pid),
|
|
431
|
+
encoding="utf-8",
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
def _start_tendermint_process(
|
|
435
|
+
self, env: Dict, working_dir: Path
|
|
436
|
+
) -> subprocess.Popen:
|
|
437
|
+
raise NotImplementedError
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
class PyInstallerHostDeploymentRunnerMac(PyInstallerHostDeploymentRunner):
|
|
441
|
+
"""Mac deployment runner."""
|
|
442
|
+
|
|
443
|
+
def _start_agent_process(
|
|
444
|
+
self, env: Dict, working_dir: Path, password: str
|
|
445
|
+
) -> subprocess.Popen:
|
|
446
|
+
"""Start agent process."""
|
|
447
|
+
agent_runner_log_file = self._open_agent_runner_log_file()
|
|
448
|
+
process = subprocess.Popen( # pylint: disable=consider-using-with,subprocess-popen-preexec-fn # nosec
|
|
449
|
+
args=self.get_agent_start_args(password=password),
|
|
450
|
+
cwd=working_dir / "agent",
|
|
451
|
+
stdout=agent_runner_log_file,
|
|
452
|
+
stderr=agent_runner_log_file,
|
|
453
|
+
env=env,
|
|
454
|
+
preexec_fn=os.setpgrp,
|
|
455
|
+
)
|
|
456
|
+
return process
|
|
457
|
+
|
|
458
|
+
def _start_tendermint_process(
|
|
459
|
+
self, env: Dict, working_dir: Path
|
|
460
|
+
) -> subprocess.Popen:
|
|
461
|
+
"""Start tendermint process."""
|
|
462
|
+
env = {
|
|
463
|
+
**env,
|
|
464
|
+
}
|
|
465
|
+
env["PATH"] = os.path.dirname(sys.executable) + ":" + os.environ["PATH"]
|
|
466
|
+
tm_log_file = self._open_tendermint_log_file()
|
|
467
|
+
process = subprocess.Popen( # pylint: disable=consider-using-with,subprocess-popen-preexec-fn # nosec
|
|
468
|
+
args=[self._tendermint_bin],
|
|
469
|
+
cwd=working_dir,
|
|
470
|
+
stdout=tm_log_file,
|
|
471
|
+
stderr=tm_log_file,
|
|
472
|
+
env=env,
|
|
473
|
+
preexec_fn=os.setpgrp, # pylint: disable=subprocess-popen-preexec-fn # nosec
|
|
474
|
+
)
|
|
475
|
+
return process
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
class PyInstallerHostDeploymentRunnerWindows(PyInstallerHostDeploymentRunner):
|
|
479
|
+
"""Windows deployment runner."""
|
|
480
|
+
|
|
481
|
+
def __init__(self, work_directory: Path, is_aea: bool) -> None:
|
|
482
|
+
"""Init the runner."""
|
|
483
|
+
super().__init__(work_directory, is_aea=is_aea)
|
|
484
|
+
self._job = self.set_windows_object_job()
|
|
485
|
+
|
|
486
|
+
@staticmethod
|
|
487
|
+
def set_windows_object_job() -> Any:
|
|
488
|
+
"""Set windows job object to handle sub processes."""
|
|
489
|
+
from ctypes import ( # type: ignore # pylint:disable=import-outside-toplevel,reimported
|
|
490
|
+
wintypes,
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
kernel32 = ctypes.windll.kernel32 # type: ignore
|
|
494
|
+
|
|
495
|
+
class JOBOBJECT_BASIC_LIMIT_INFORMATION(
|
|
496
|
+
ctypes.Structure
|
|
497
|
+
): # pylint: disable=missing-class-docstring
|
|
498
|
+
_fields_ = [
|
|
499
|
+
("PerProcessUserTimeLimit", wintypes.LARGE_INTEGER),
|
|
500
|
+
("PerJobUserTimeLimit", wintypes.LARGE_INTEGER),
|
|
501
|
+
("LimitFlags", wintypes.DWORD),
|
|
502
|
+
("MinimumWorkingSetSize", ctypes.c_size_t),
|
|
503
|
+
("MaximumWorkingSetSize", ctypes.c_size_t),
|
|
504
|
+
("ActiveProcessLimit", wintypes.DWORD),
|
|
505
|
+
("Affinity", ctypes.POINTER(wintypes.ULONG)),
|
|
506
|
+
("PriorityClass", wintypes.DWORD),
|
|
507
|
+
("SchedulingClass", wintypes.DWORD),
|
|
508
|
+
]
|
|
509
|
+
|
|
510
|
+
class IO_COUNTERS(ctypes.Structure): # pylint: disable=missing-class-docstring
|
|
511
|
+
_fields_ = [
|
|
512
|
+
("ReadOperationCount", ctypes.c_ulonglong),
|
|
513
|
+
("WriteOperationCount", ctypes.c_ulonglong),
|
|
514
|
+
("OtherOperationCount", ctypes.c_ulonglong),
|
|
515
|
+
("ReadTransferCount", ctypes.c_ulonglong),
|
|
516
|
+
("WriteTransferCount", ctypes.c_ulonglong),
|
|
517
|
+
("OtherTransferCount", ctypes.c_ulonglong),
|
|
518
|
+
]
|
|
519
|
+
|
|
520
|
+
class JOBOBJECT_EXTENDED_LIMIT_INFORMATION(
|
|
521
|
+
ctypes.Structure
|
|
522
|
+
): # pylint: disable=missing-class-docstring
|
|
523
|
+
_fields_ = [
|
|
524
|
+
("BasicLimitInformation", JOBOBJECT_BASIC_LIMIT_INFORMATION),
|
|
525
|
+
("IoInfo", IO_COUNTERS),
|
|
526
|
+
("ProcessMemoryLimit", ctypes.c_size_t),
|
|
527
|
+
("JobMemoryLimit", ctypes.c_size_t),
|
|
528
|
+
("PeakProcessMemoryUsed", ctypes.c_size_t),
|
|
529
|
+
("PeakJobMemoryUsed", ctypes.c_size_t),
|
|
530
|
+
]
|
|
531
|
+
|
|
532
|
+
# Создаем Job Object
|
|
533
|
+
job = kernel32.CreateJobObjectW(None, None)
|
|
534
|
+
if not job:
|
|
535
|
+
raise ctypes.WinError() # type: ignore
|
|
536
|
+
|
|
537
|
+
# Настраиваем автоматическое завершение процессов при закрытии Job
|
|
538
|
+
info = JOBOBJECT_EXTENDED_LIMIT_INFORMATION()
|
|
539
|
+
info.BasicLimitInformation.LimitFlags = (
|
|
540
|
+
0x2000 # JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
if not kernel32.SetInformationJobObject(
|
|
544
|
+
job,
|
|
545
|
+
9, # JobObjectExtendedLimitInformation
|
|
546
|
+
ctypes.byref(info),
|
|
547
|
+
ctypes.sizeof(info),
|
|
548
|
+
):
|
|
549
|
+
kernel32.CloseHandle(job)
|
|
550
|
+
raise ctypes.WinError() # type: ignore
|
|
551
|
+
|
|
552
|
+
return job
|
|
553
|
+
|
|
554
|
+
def assign_to_job(self, pid: int) -> None:
|
|
555
|
+
"""Windows-only: привязывает процесс к Job Object."""
|
|
556
|
+
ctypes.windll.kernel32.AssignProcessToJobObject(self._job, pid) # type: ignore
|
|
557
|
+
|
|
558
|
+
@property
|
|
559
|
+
def _tendermint_bin(self) -> str:
|
|
560
|
+
"""Return tendermint path."""
|
|
561
|
+
return str(Path(os.path.dirname(sys.executable)) / "tendermint_win.exe") # type: ignore # pylint: disable=protected-access
|
|
562
|
+
|
|
563
|
+
def _start_agent_process(
|
|
564
|
+
self, env: Dict, working_dir: Path, password: str
|
|
565
|
+
) -> subprocess.Popen:
|
|
566
|
+
"""Start agent process."""
|
|
567
|
+
agent_runner_log_file = self._open_agent_runner_log_file()
|
|
568
|
+
process = subprocess.Popen( # pylint: disable=consider-using-with # nosec
|
|
569
|
+
args=self.get_agent_start_args(password=password),
|
|
570
|
+
cwd=working_dir / "agent",
|
|
571
|
+
stdout=agent_runner_log_file,
|
|
572
|
+
stderr=agent_runner_log_file,
|
|
573
|
+
env=env,
|
|
574
|
+
creationflags=0x00000200, # Detach process from the main process
|
|
575
|
+
)
|
|
576
|
+
self.assign_to_job(process._handle) # type: ignore # pylint: disable=protected-access
|
|
577
|
+
return process
|
|
578
|
+
|
|
579
|
+
def _start_tendermint_process(
|
|
580
|
+
self, env: Dict, working_dir: Path
|
|
581
|
+
) -> subprocess.Popen:
|
|
582
|
+
"""Start tendermint process."""
|
|
583
|
+
env = {
|
|
584
|
+
**env,
|
|
585
|
+
}
|
|
586
|
+
tm_log_file = self._open_tendermint_log_file()
|
|
587
|
+
env["PATH"] = os.path.dirname(sys.executable) + ";" + os.environ["PATH"]
|
|
588
|
+
|
|
589
|
+
process = subprocess.Popen( # pylint: disable=consider-using-with # nosec
|
|
590
|
+
args=[self._tendermint_bin],
|
|
591
|
+
cwd=working_dir,
|
|
592
|
+
stdout=tm_log_file,
|
|
593
|
+
stderr=tm_log_file,
|
|
594
|
+
env=env,
|
|
595
|
+
creationflags=0x00000200, # Detach process from the main process
|
|
596
|
+
)
|
|
597
|
+
self.assign_to_job(process._handle) # type: ignore # pylint: disable=protected-access
|
|
598
|
+
return process
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
class HostPythonHostDeploymentRunner(BaseDeploymentRunner):
|
|
602
|
+
"""Deployment runner for host installed python."""
|
|
603
|
+
|
|
604
|
+
@property
|
|
605
|
+
def _agent_runner_bin(self) -> str:
|
|
606
|
+
"""Return aea_bin path."""
|
|
607
|
+
if self._is_aea:
|
|
608
|
+
return str(self._venv_dir / "bin" / "aea")
|
|
609
|
+
|
|
610
|
+
service_dir = self._work_directory.parent
|
|
611
|
+
agent_runner_bin = get_agent_runner_path(service_dir=service_dir)
|
|
612
|
+
return str(agent_runner_bin)
|
|
613
|
+
|
|
614
|
+
def _start_agent(self, password: str) -> None:
|
|
615
|
+
"""Start agent process."""
|
|
616
|
+
working_dir = self._work_directory
|
|
617
|
+
env = json.loads((working_dir / "agent.json").read_text(encoding="utf-8"))
|
|
618
|
+
env["PYTHONUTF8"] = "1"
|
|
619
|
+
env["PYTHONIOENCODING"] = "utf8"
|
|
620
|
+
agent_runner_log_file = self._open_agent_runner_log_file()
|
|
621
|
+
|
|
622
|
+
process = subprocess.Popen( # pylint: disable=consider-using-with # nosec
|
|
623
|
+
args=self.get_agent_start_args(password=password),
|
|
624
|
+
cwd=str(working_dir / "agent"),
|
|
625
|
+
env={**os.environ, **env},
|
|
626
|
+
stdout=agent_runner_log_file,
|
|
627
|
+
stderr=agent_runner_log_file,
|
|
628
|
+
creationflags=(
|
|
629
|
+
0x00000008 if platform.system() == "Windows" else 0
|
|
630
|
+
), # Detach process from the main process
|
|
631
|
+
)
|
|
632
|
+
(working_dir / "agent.pid").write_text(
|
|
633
|
+
data=str(process.pid),
|
|
634
|
+
encoding="utf-8",
|
|
635
|
+
)
|
|
636
|
+
|
|
637
|
+
def _start_tendermint(self) -> None:
|
|
638
|
+
"""Start tendermint process."""
|
|
639
|
+
working_dir = self._work_directory
|
|
640
|
+
env = json.loads((working_dir / "tendermint.json").read_text(encoding="utf-8"))
|
|
641
|
+
env["PYTHONUTF8"] = "1"
|
|
642
|
+
env["PYTHONIOENCODING"] = "utf8"
|
|
643
|
+
|
|
644
|
+
process = subprocess.Popen( # pylint: disable=consider-using-with # nosec
|
|
645
|
+
args=[
|
|
646
|
+
str(self._venv_dir / "bin" / "flask"),
|
|
647
|
+
"run",
|
|
648
|
+
"--host",
|
|
649
|
+
"localhost",
|
|
650
|
+
"--port",
|
|
651
|
+
"8080",
|
|
652
|
+
],
|
|
653
|
+
cwd=working_dir,
|
|
654
|
+
stdout=subprocess.DEVNULL,
|
|
655
|
+
stderr=subprocess.DEVNULL,
|
|
656
|
+
env={**os.environ, **env},
|
|
657
|
+
creationflags=(
|
|
658
|
+
0x00000008 if platform.system() == "Windows" else 0
|
|
659
|
+
), # Detach process from the main process
|
|
660
|
+
)
|
|
661
|
+
(working_dir / "tendermint.pid").write_text(
|
|
662
|
+
data=str(process.pid),
|
|
663
|
+
encoding="utf-8",
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
@property
|
|
667
|
+
def _venv_dir(self) -> Path:
|
|
668
|
+
"""Get venv dir for aea."""
|
|
669
|
+
return self._work_directory / "venv"
|
|
670
|
+
|
|
671
|
+
def _setup_venv(self) -> None:
|
|
672
|
+
"""Perform venv setup, install deps."""
|
|
673
|
+
if not self._is_aea:
|
|
674
|
+
return
|
|
675
|
+
|
|
676
|
+
self._venv_dir.mkdir(exist_ok=True)
|
|
677
|
+
venv_cli(args=[str(self._venv_dir)])
|
|
678
|
+
pbin = str(self._venv_dir / "bin" / "python")
|
|
679
|
+
# Install agent dependencies
|
|
680
|
+
self._run_cmd(
|
|
681
|
+
args=[
|
|
682
|
+
pbin,
|
|
683
|
+
"-m",
|
|
684
|
+
"pip",
|
|
685
|
+
"install",
|
|
686
|
+
f"open-autonomy[all]=={autonomy_version}",
|
|
687
|
+
f"open-aea-ledger-ethereum=={aea_version}",
|
|
688
|
+
f"open-aea-ledger-ethereum-flashbots=={aea_version}",
|
|
689
|
+
f"open-aea-ledger-cosmos=={aea_version}",
|
|
690
|
+
# Install tendermint dependencies
|
|
691
|
+
"flask",
|
|
692
|
+
"requests",
|
|
693
|
+
"multiaddr==0.0.9", # TODO: remove when pinned on open-aea
|
|
694
|
+
],
|
|
695
|
+
)
|
|
696
|
+
|
|
697
|
+
def _setup_agent(self, password: str) -> None:
|
|
698
|
+
"""Prepare agent."""
|
|
699
|
+
multiprocessing.set_start_method("spawn")
|
|
700
|
+
self._setup_venv()
|
|
701
|
+
super()._setup_agent(password=password)
|
|
702
|
+
if not self._is_aea:
|
|
703
|
+
return
|
|
704
|
+
|
|
705
|
+
# Install agent dependencies
|
|
706
|
+
self._run_cmd(
|
|
707
|
+
args=[
|
|
708
|
+
self._agent_runner_bin,
|
|
709
|
+
"-v",
|
|
710
|
+
"debug",
|
|
711
|
+
"install",
|
|
712
|
+
"--timeout",
|
|
713
|
+
"600",
|
|
714
|
+
],
|
|
715
|
+
cwd=self._work_directory / "agent",
|
|
716
|
+
)
|
|
717
|
+
|
|
718
|
+
|
|
719
|
+
class States(Enum):
|
|
720
|
+
"""Service deployment states."""
|
|
721
|
+
|
|
722
|
+
NONE = 0
|
|
723
|
+
STARTING = 1
|
|
724
|
+
STARTED = 2
|
|
725
|
+
STOPPING = 3
|
|
726
|
+
STOPPED = 4
|
|
727
|
+
ERROR = 5
|
|
728
|
+
|
|
729
|
+
|
|
730
|
+
class DeploymentManager:
|
|
731
|
+
"""Deployment manager to run and stop deployments."""
|
|
732
|
+
|
|
733
|
+
def __init__(self) -> None:
|
|
734
|
+
"""Init the deployment manager."""
|
|
735
|
+
self._deployment_runner_class = self._get_host_deployment_runner_class()
|
|
736
|
+
self._is_stopping = False
|
|
737
|
+
self.logger = setup_logger(name="operate.deployment_manager")
|
|
738
|
+
self._states: Dict[Path, States] = {}
|
|
739
|
+
|
|
740
|
+
def _get_deployment_runner(
|
|
741
|
+
self, build_dir: Path, is_aea: bool
|
|
742
|
+
) -> BaseDeploymentRunner:
|
|
743
|
+
"""Get deploymnent runner instance."""
|
|
744
|
+
return self._deployment_runner_class(build_dir, is_aea=is_aea)
|
|
745
|
+
|
|
746
|
+
@staticmethod
|
|
747
|
+
def _get_host_deployment_runner_class() -> Type[BaseDeploymentRunner]:
|
|
748
|
+
"""Return depoyment runner class according to running env."""
|
|
749
|
+
|
|
750
|
+
if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
|
|
751
|
+
# pyinstaller inside!
|
|
752
|
+
if platform.system() == "Darwin":
|
|
753
|
+
return PyInstallerHostDeploymentRunnerMac
|
|
754
|
+
if platform.system() == "Windows":
|
|
755
|
+
return PyInstallerHostDeploymentRunnerWindows
|
|
756
|
+
raise ValueError(f"Platform not supported {platform.system()}")
|
|
757
|
+
|
|
758
|
+
return HostPythonHostDeploymentRunner
|
|
759
|
+
|
|
760
|
+
def stop(self) -> None:
|
|
761
|
+
"""Stop deploment manager."""
|
|
762
|
+
self.logger.info("Stop deployment manager")
|
|
763
|
+
self._is_stopping = True
|
|
764
|
+
|
|
765
|
+
def get_state(self, build_dir: Path) -> States:
|
|
766
|
+
"""Get state of the deployment."""
|
|
767
|
+
return self._states.get(build_dir) or States.NONE
|
|
768
|
+
|
|
769
|
+
def check_ipfs_connection_works(self) -> None:
|
|
770
|
+
"""Check ipfs works and there is a good net connection."""
|
|
771
|
+
self.logger.info("Doing network connection check by test call to ipfs server.")
|
|
772
|
+
for i in range(3):
|
|
773
|
+
try:
|
|
774
|
+
requests.get(constants.IPFS_CHECK_URL, timeout=60)
|
|
775
|
+
return
|
|
776
|
+
except OSError:
|
|
777
|
+
self.logger.exception(
|
|
778
|
+
"failed to connect to ipfs to test connection. OSError, critical!"
|
|
779
|
+
)
|
|
780
|
+
raise
|
|
781
|
+
except Exception: # pylint: disable=broad-except
|
|
782
|
+
self.logger.exception(
|
|
783
|
+
"failed to connect to ipfs to test connection. do another try"
|
|
784
|
+
)
|
|
785
|
+
time.sleep(i * 5)
|
|
786
|
+
self.logger.error(
|
|
787
|
+
"failed to connect to ipfs to test connection. no attempts left. raise error"
|
|
788
|
+
)
|
|
789
|
+
raise RuntimeError(
|
|
790
|
+
"Failed to perform test connection to ipfs to check network connection!"
|
|
791
|
+
)
|
|
792
|
+
|
|
793
|
+
def run_deployment(
|
|
794
|
+
self, build_dir: Path, password: str, is_aea: bool = True
|
|
795
|
+
) -> None:
|
|
796
|
+
"""Run deployment."""
|
|
797
|
+
if self._is_stopping:
|
|
798
|
+
raise RuntimeError("deployment manager stopped")
|
|
799
|
+
if self.get_state(build_dir=build_dir) in [States.STARTING, States.STOPPING]:
|
|
800
|
+
raise ValueError("Service already in transition")
|
|
801
|
+
|
|
802
|
+
# doing pre check for ipfs works fine, also network connection is ok.
|
|
803
|
+
self.check_ipfs_connection_works()
|
|
804
|
+
|
|
805
|
+
self.logger.info(f"Starting deployment {build_dir}...")
|
|
806
|
+
self._states[build_dir] = States.STARTING
|
|
807
|
+
try:
|
|
808
|
+
deployment_runner = self._get_deployment_runner(
|
|
809
|
+
build_dir=build_dir, is_aea=is_aea
|
|
810
|
+
)
|
|
811
|
+
deployment_runner.start(password=password)
|
|
812
|
+
self.logger.info(f"Started deployment {build_dir}")
|
|
813
|
+
self._states[build_dir] = States.STARTED
|
|
814
|
+
except Exception: # pylint: disable=broad-except
|
|
815
|
+
self.logger.exception(
|
|
816
|
+
f"Starting deployment failed {build_dir}. so try to stop"
|
|
817
|
+
)
|
|
818
|
+
self._states[build_dir] = States.ERROR
|
|
819
|
+
self.stop_deployment(build_dir=build_dir, force=True)
|
|
820
|
+
|
|
821
|
+
if self._is_stopping:
|
|
822
|
+
self.logger.warning(
|
|
823
|
+
f"Deployment at {build_dir} started when it was going to stop, so stop it"
|
|
824
|
+
)
|
|
825
|
+
self.stop_deployment(build_dir=build_dir, force=True)
|
|
826
|
+
|
|
827
|
+
def stop_deployment(
|
|
828
|
+
self, build_dir: Path, force: bool = False, is_aea: bool = True
|
|
829
|
+
) -> None:
|
|
830
|
+
"""Stop the deployment."""
|
|
831
|
+
if (
|
|
832
|
+
self.get_state(build_dir=build_dir) in [States.STARTING, States.STOPPING]
|
|
833
|
+
and not force
|
|
834
|
+
):
|
|
835
|
+
raise ValueError("Service already in transition")
|
|
836
|
+
self.logger.info(f"Stopping deployment {build_dir}...")
|
|
837
|
+
self._states[build_dir] = States.STOPPING
|
|
838
|
+
deployment_runner = self._get_deployment_runner(
|
|
839
|
+
build_dir=build_dir, is_aea=is_aea
|
|
840
|
+
)
|
|
841
|
+
try:
|
|
842
|
+
deployment_runner.stop()
|
|
843
|
+
self.logger.info(f"Stopped deployment {build_dir}...")
|
|
844
|
+
self._states[build_dir] = States.STOPPED
|
|
845
|
+
except Exception:
|
|
846
|
+
self.logger.exception(f"Stopping deployment failed {build_dir}...")
|
|
847
|
+
self._states[build_dir] = States.ERROR
|
|
848
|
+
raise
|
|
849
|
+
|
|
850
|
+
|
|
851
|
+
deployment_manager = DeploymentManager()
|
|
852
|
+
|
|
853
|
+
|
|
854
|
+
def run_host_deployment(build_dir: Path, password: str, is_aea: bool = True) -> None:
|
|
855
|
+
"""Run host deployment."""
|
|
856
|
+
deployment_manager.run_deployment(
|
|
857
|
+
build_dir=build_dir, password=password, is_aea=is_aea
|
|
858
|
+
)
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
def stop_host_deployment(build_dir: Path, is_aea: bool = True) -> None:
|
|
862
|
+
"""Stop host deployment."""
|
|
863
|
+
deployment_manager.stop_deployment(build_dir=build_dir, is_aea=is_aea)
|
|
864
|
+
|
|
865
|
+
|
|
866
|
+
def stop_deployment_manager() -> None:
|
|
867
|
+
"""Stop deployment manager."""
|
|
868
|
+
deployment_manager.stop()
|