olas-operate-middleware 0.12.2__py3-none-any.whl → 0.13.0__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.12.2.dist-info → olas_operate_middleware-0.13.0.dist-info}/METADATA +1 -1
- {olas_operate_middleware-0.12.2.dist-info → olas_operate_middleware-0.13.0.dist-info}/RECORD +20 -20
- operate/cli.py +73 -18
- operate/keys.py +9 -3
- operate/ledger/profiles.py +11 -0
- operate/migration.py +43 -11
- operate/operate_types.py +16 -0
- operate/quickstart/reset_password.py +1 -2
- operate/quickstart/run_service.py +15 -3
- operate/quickstart/stop_service.py +9 -2
- operate/services/agent_runner.py +19 -29
- operate/services/deployment_runner.py +83 -49
- operate/services/funding_manager.py +5 -3
- operate/services/manage.py +22 -7
- operate/services/service.py +66 -34
- operate/wallet/master.py +2 -2
- operate/wallet/wallet_recovery_manager.py +281 -36
- {olas_operate_middleware-0.12.2.dist-info → olas_operate_middleware-0.13.0.dist-info}/WHEEL +0 -0
- {olas_operate_middleware-0.12.2.dist-info → olas_operate_middleware-0.13.0.dist-info}/entry_points.txt +0 -0
- {olas_operate_middleware-0.12.2.dist-info → olas_operate_middleware-0.13.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -105,6 +105,11 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
|
|
|
105
105
|
START_TRIES = constants.DEPLOYMENT_START_TRIES_NUM
|
|
106
106
|
logger = setup_logger(name="operate.base_deployment_runner")
|
|
107
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
|
+
|
|
108
113
|
def _open_agent_runner_log_file(self) -> TextIOWrapper:
|
|
109
114
|
"""Open agent_runner.log file."""
|
|
110
115
|
return (
|
|
@@ -186,6 +191,21 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
|
|
|
186
191
|
"""Setup agent."""
|
|
187
192
|
working_dir = self._work_directory
|
|
188
193
|
env = self._prepare_agent_env()
|
|
194
|
+
agent_alias_name = "agent"
|
|
195
|
+
agent_dir_full_path = Path(working_dir) / agent_alias_name
|
|
196
|
+
if not self._is_aea:
|
|
197
|
+
if agent_dir_full_path.exists():
|
|
198
|
+
# remove if exists before fetching! can have issues with retry mechanism of multiple start attempts
|
|
199
|
+
with suppress(Exception):
|
|
200
|
+
shutil.rmtree(agent_dir_full_path, ignore_errors=True)
|
|
201
|
+
|
|
202
|
+
# Add keys
|
|
203
|
+
agent_dir_full_path.mkdir(exist_ok=True, parents=True)
|
|
204
|
+
shutil.copy(
|
|
205
|
+
working_dir / "ethereum_private_key.txt",
|
|
206
|
+
working_dir / "agent" / "ethereum_private_key.txt",
|
|
207
|
+
)
|
|
208
|
+
return
|
|
189
209
|
|
|
190
210
|
self._run_aea_command(
|
|
191
211
|
"init",
|
|
@@ -199,10 +219,6 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
|
|
|
199
219
|
cwd=working_dir,
|
|
200
220
|
)
|
|
201
221
|
|
|
202
|
-
agent_alias_name = "agent"
|
|
203
|
-
|
|
204
|
-
agent_dir_full_path = Path(working_dir) / agent_alias_name
|
|
205
|
-
|
|
206
222
|
if agent_dir_full_path.exists():
|
|
207
223
|
# remove if exists before fetching! can have issues with retry mechanism of multiple start attempts
|
|
208
224
|
with suppress(Exception):
|
|
@@ -264,13 +280,16 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
|
|
|
264
280
|
def _start(self, password: str) -> None:
|
|
265
281
|
"""Start the deployment."""
|
|
266
282
|
self._setup_agent(password=password)
|
|
267
|
-
self.
|
|
283
|
+
if self._is_aea:
|
|
284
|
+
self._start_tendermint()
|
|
285
|
+
|
|
268
286
|
self._start_agent(password=password)
|
|
269
287
|
|
|
270
288
|
def stop(self) -> None:
|
|
271
289
|
"""Stop the deployment."""
|
|
272
290
|
self._stop_agent()
|
|
273
|
-
self.
|
|
291
|
+
if self._is_aea:
|
|
292
|
+
self._stop_tendermint()
|
|
274
293
|
|
|
275
294
|
def _stop_agent(self) -> None:
|
|
276
295
|
"""Start process."""
|
|
@@ -313,6 +332,24 @@ class BaseDeploymentRunner(AbstractDeploymentRunner, metaclass=ABCMeta):
|
|
|
313
332
|
"""Return aea_bin path."""
|
|
314
333
|
raise NotImplementedError
|
|
315
334
|
|
|
335
|
+
def get_agent_start_args(self, password: str) -> List[str]:
|
|
336
|
+
"""Return agent start arguments."""
|
|
337
|
+
return (
|
|
338
|
+
[self._agent_runner_bin]
|
|
339
|
+
+ (
|
|
340
|
+
[
|
|
341
|
+
"-s",
|
|
342
|
+
"run",
|
|
343
|
+
]
|
|
344
|
+
if self._is_aea
|
|
345
|
+
else []
|
|
346
|
+
)
|
|
347
|
+
+ [
|
|
348
|
+
"--password",
|
|
349
|
+
password,
|
|
350
|
+
]
|
|
351
|
+
)
|
|
352
|
+
|
|
316
353
|
|
|
317
354
|
class PyInstallerHostDeploymentRunner(BaseDeploymentRunner):
|
|
318
355
|
"""Deployment runner within pyinstaller env."""
|
|
@@ -320,16 +357,8 @@ class PyInstallerHostDeploymentRunner(BaseDeploymentRunner):
|
|
|
320
357
|
@property
|
|
321
358
|
def _agent_runner_bin(self) -> str:
|
|
322
359
|
"""Return aea_bin path."""
|
|
323
|
-
env = json.loads(
|
|
324
|
-
(self._work_directory / "agent.json").read_text(encoding="utf-8")
|
|
325
|
-
)
|
|
326
|
-
|
|
327
|
-
agent_publicid_str = env["AEA_AGENT"]
|
|
328
360
|
service_dir = self._work_directory.parent
|
|
329
|
-
|
|
330
|
-
agent_runner_bin = get_agent_runner_path(
|
|
331
|
-
service_dir=service_dir, agent_public_id_str=agent_publicid_str
|
|
332
|
-
)
|
|
361
|
+
agent_runner_bin = get_agent_runner_path(service_dir=service_dir)
|
|
333
362
|
return str(agent_runner_bin)
|
|
334
363
|
|
|
335
364
|
@property
|
|
@@ -393,13 +422,7 @@ class PyInstallerHostDeploymentRunnerMac(PyInstallerHostDeploymentRunner):
|
|
|
393
422
|
"""Start agent process."""
|
|
394
423
|
agent_runner_log_file = self._open_agent_runner_log_file()
|
|
395
424
|
process = subprocess.Popen( # pylint: disable=consider-using-with,subprocess-popen-preexec-fn # nosec
|
|
396
|
-
args=
|
|
397
|
-
self._agent_runner_bin,
|
|
398
|
-
"-s",
|
|
399
|
-
"run",
|
|
400
|
-
"--password",
|
|
401
|
-
password,
|
|
402
|
-
],
|
|
425
|
+
args=self.get_agent_start_args(password=password),
|
|
403
426
|
cwd=working_dir / "agent",
|
|
404
427
|
stdout=agent_runner_log_file,
|
|
405
428
|
stderr=agent_runner_log_file,
|
|
@@ -431,9 +454,9 @@ class PyInstallerHostDeploymentRunnerMac(PyInstallerHostDeploymentRunner):
|
|
|
431
454
|
class PyInstallerHostDeploymentRunnerWindows(PyInstallerHostDeploymentRunner):
|
|
432
455
|
"""Windows deployment runner."""
|
|
433
456
|
|
|
434
|
-
def __init__(self, work_directory: Path) -> None:
|
|
457
|
+
def __init__(self, work_directory: Path, is_aea: bool) -> None:
|
|
435
458
|
"""Init the runner."""
|
|
436
|
-
super().__init__(work_directory)
|
|
459
|
+
super().__init__(work_directory, is_aea=is_aea)
|
|
437
460
|
self._job = self.set_windows_object_job()
|
|
438
461
|
|
|
439
462
|
@staticmethod
|
|
@@ -519,13 +542,7 @@ class PyInstallerHostDeploymentRunnerWindows(PyInstallerHostDeploymentRunner):
|
|
|
519
542
|
"""Start agent process."""
|
|
520
543
|
agent_runner_log_file = self._open_agent_runner_log_file()
|
|
521
544
|
process = subprocess.Popen( # pylint: disable=consider-using-with # nosec
|
|
522
|
-
args=
|
|
523
|
-
self._agent_runner_bin,
|
|
524
|
-
"-s",
|
|
525
|
-
"run",
|
|
526
|
-
"--password",
|
|
527
|
-
password,
|
|
528
|
-
], # TODO: Patch for Windows failing hash
|
|
545
|
+
args=self.get_agent_start_args(password=password),
|
|
529
546
|
cwd=working_dir / "agent",
|
|
530
547
|
stdout=agent_runner_log_file,
|
|
531
548
|
stderr=agent_runner_log_file,
|
|
@@ -562,7 +579,12 @@ class HostPythonHostDeploymentRunner(BaseDeploymentRunner):
|
|
|
562
579
|
@property
|
|
563
580
|
def _agent_runner_bin(self) -> str:
|
|
564
581
|
"""Return aea_bin path."""
|
|
565
|
-
|
|
582
|
+
if self._is_aea:
|
|
583
|
+
return str(self._venv_dir / "bin" / "aea")
|
|
584
|
+
|
|
585
|
+
service_dir = self._work_directory.parent
|
|
586
|
+
agent_runner_bin = get_agent_runner_path(service_dir=service_dir)
|
|
587
|
+
return str(agent_runner_bin)
|
|
566
588
|
|
|
567
589
|
def _start_agent(self, password: str) -> None:
|
|
568
590
|
"""Start agent process."""
|
|
@@ -573,13 +595,7 @@ class HostPythonHostDeploymentRunner(BaseDeploymentRunner):
|
|
|
573
595
|
agent_runner_log_file = self._open_agent_runner_log_file()
|
|
574
596
|
|
|
575
597
|
process = subprocess.Popen( # pylint: disable=consider-using-with # nosec
|
|
576
|
-
args=
|
|
577
|
-
self._agent_runner_bin,
|
|
578
|
-
"-s",
|
|
579
|
-
"run",
|
|
580
|
-
"--password",
|
|
581
|
-
password,
|
|
582
|
-
], # TODO: Patch for Windows failing hash
|
|
598
|
+
args=self.get_agent_start_args(password=password),
|
|
583
599
|
cwd=str(working_dir / "agent"),
|
|
584
600
|
env={**os.environ, **env},
|
|
585
601
|
stdout=agent_runner_log_file,
|
|
@@ -629,6 +645,9 @@ class HostPythonHostDeploymentRunner(BaseDeploymentRunner):
|
|
|
629
645
|
|
|
630
646
|
def _setup_venv(self) -> None:
|
|
631
647
|
"""Perform venv setup, install deps."""
|
|
648
|
+
if not self._is_aea:
|
|
649
|
+
return
|
|
650
|
+
|
|
632
651
|
self._venv_dir.mkdir(exist_ok=True)
|
|
633
652
|
venv_cli(args=[str(self._venv_dir)])
|
|
634
653
|
pbin = str(self._venv_dir / "bin" / "python")
|
|
@@ -655,6 +674,9 @@ class HostPythonHostDeploymentRunner(BaseDeploymentRunner):
|
|
|
655
674
|
multiprocessing.set_start_method("spawn")
|
|
656
675
|
self._setup_venv()
|
|
657
676
|
super()._setup_agent(password=password)
|
|
677
|
+
if not self._is_aea:
|
|
678
|
+
return
|
|
679
|
+
|
|
658
680
|
# Install agent dependencies
|
|
659
681
|
self._run_cmd(
|
|
660
682
|
args=[
|
|
@@ -690,9 +712,11 @@ class DeploymentManager:
|
|
|
690
712
|
self.logger = setup_logger(name="operate.deployment_manager")
|
|
691
713
|
self._states: Dict[Path, States] = {}
|
|
692
714
|
|
|
693
|
-
def _get_deployment_runner(
|
|
715
|
+
def _get_deployment_runner(
|
|
716
|
+
self, build_dir: Path, is_aea: bool
|
|
717
|
+
) -> BaseDeploymentRunner:
|
|
694
718
|
"""Get deploymnent runner instance."""
|
|
695
|
-
return self._deployment_runner_class(build_dir)
|
|
719
|
+
return self._deployment_runner_class(build_dir, is_aea=is_aea)
|
|
696
720
|
|
|
697
721
|
@staticmethod
|
|
698
722
|
def _get_host_deployment_runner_class() -> Type[BaseDeploymentRunner]:
|
|
@@ -741,7 +765,9 @@ class DeploymentManager:
|
|
|
741
765
|
"Failed to perform test connection to ipfs to check network connection!"
|
|
742
766
|
)
|
|
743
767
|
|
|
744
|
-
def run_deployment(
|
|
768
|
+
def run_deployment(
|
|
769
|
+
self, build_dir: Path, password: str, is_aea: bool = True
|
|
770
|
+
) -> None:
|
|
745
771
|
"""Run deployment."""
|
|
746
772
|
if self._is_stopping:
|
|
747
773
|
raise RuntimeError("deployment manager stopped")
|
|
@@ -754,7 +780,9 @@ class DeploymentManager:
|
|
|
754
780
|
self.logger.info(f"Starting deployment {build_dir}...")
|
|
755
781
|
self._states[build_dir] = States.STARTING
|
|
756
782
|
try:
|
|
757
|
-
deployment_runner = self._get_deployment_runner(
|
|
783
|
+
deployment_runner = self._get_deployment_runner(
|
|
784
|
+
build_dir=build_dir, is_aea=is_aea
|
|
785
|
+
)
|
|
758
786
|
deployment_runner.start(password=password)
|
|
759
787
|
self.logger.info(f"Started deployment {build_dir}")
|
|
760
788
|
self._states[build_dir] = States.STARTED
|
|
@@ -771,7 +799,9 @@ class DeploymentManager:
|
|
|
771
799
|
)
|
|
772
800
|
self.stop_deployment(build_dir=build_dir, force=True)
|
|
773
801
|
|
|
774
|
-
def stop_deployment(
|
|
802
|
+
def stop_deployment(
|
|
803
|
+
self, build_dir: Path, force: bool = False, is_aea: bool = True
|
|
804
|
+
) -> None:
|
|
775
805
|
"""Stop the deployment."""
|
|
776
806
|
if (
|
|
777
807
|
self.get_state(build_dir=build_dir) in [States.STARTING, States.STOPPING]
|
|
@@ -780,7 +810,9 @@ class DeploymentManager:
|
|
|
780
810
|
raise ValueError("Service already in transition")
|
|
781
811
|
self.logger.info(f"Stopping deployment {build_dir}...")
|
|
782
812
|
self._states[build_dir] = States.STOPPING
|
|
783
|
-
deployment_runner = self._get_deployment_runner(
|
|
813
|
+
deployment_runner = self._get_deployment_runner(
|
|
814
|
+
build_dir=build_dir, is_aea=is_aea
|
|
815
|
+
)
|
|
784
816
|
try:
|
|
785
817
|
deployment_runner.stop()
|
|
786
818
|
self.logger.info(f"Stopped deployment {build_dir}...")
|
|
@@ -794,14 +826,16 @@ class DeploymentManager:
|
|
|
794
826
|
deployment_manager = DeploymentManager()
|
|
795
827
|
|
|
796
828
|
|
|
797
|
-
def run_host_deployment(build_dir: Path, password: str) -> None:
|
|
829
|
+
def run_host_deployment(build_dir: Path, password: str, is_aea: bool = True) -> None:
|
|
798
830
|
"""Run host deployment."""
|
|
799
|
-
deployment_manager.run_deployment(
|
|
831
|
+
deployment_manager.run_deployment(
|
|
832
|
+
build_dir=build_dir, password=password, is_aea=is_aea
|
|
833
|
+
)
|
|
800
834
|
|
|
801
835
|
|
|
802
|
-
def stop_host_deployment(build_dir: Path) -> None:
|
|
836
|
+
def stop_host_deployment(build_dir: Path, is_aea: bool = True) -> None:
|
|
803
837
|
"""Stop host deployment."""
|
|
804
|
-
deployment_manager.stop_deployment(build_dir=build_dir)
|
|
838
|
+
deployment_manager.stop_deployment(build_dir=build_dir, is_aea=is_aea)
|
|
805
839
|
|
|
806
840
|
|
|
807
841
|
def stop_deployment_manager() -> None:
|
|
@@ -79,11 +79,13 @@ class FundingManager:
|
|
|
79
79
|
|
|
80
80
|
def __init__(
|
|
81
81
|
self,
|
|
82
|
+
keys_manager: KeysManager,
|
|
82
83
|
wallet_manager: MasterWalletManager,
|
|
83
84
|
logger: Logger,
|
|
84
85
|
funding_requests_cooldown_seconds: int = DEFAULT_FUNDING_REQUESTS_COOLDOWN_SECONDS,
|
|
85
86
|
) -> None:
|
|
86
87
|
"""Initialize funding manager."""
|
|
88
|
+
self.keys_manager = keys_manager
|
|
87
89
|
self.wallet_manager = wallet_manager
|
|
88
90
|
self.logger = logger
|
|
89
91
|
self.funding_requests_cooldown_seconds = funding_requests_cooldown_seconds
|
|
@@ -102,7 +104,7 @@ class FundingManager:
|
|
|
102
104
|
f"Draining service agents {service.name} ({service_config_id=})"
|
|
103
105
|
)
|
|
104
106
|
for agent_address in service.agent_addresses:
|
|
105
|
-
ethereum_crypto =
|
|
107
|
+
ethereum_crypto = self.keys_manager.get_crypto_instance(agent_address)
|
|
106
108
|
balance = ledger_api.get_balance(agent_address)
|
|
107
109
|
self.logger.info(
|
|
108
110
|
f"Draining {balance} (approx) {get_currency_denom(chain)} from {agent_address} (agent) to {withdrawal_address}"
|
|
@@ -167,7 +169,7 @@ class FundingManager:
|
|
|
167
169
|
|
|
168
170
|
# Safe not swapped
|
|
169
171
|
if set(owners) == set(service.agent_addresses):
|
|
170
|
-
ethereum_crypto =
|
|
172
|
+
ethereum_crypto = self.keys_manager.get_crypto_instance(
|
|
171
173
|
service.agent_addresses[0]
|
|
172
174
|
)
|
|
173
175
|
transfer_erc20_from_safe(
|
|
@@ -207,7 +209,7 @@ class FundingManager:
|
|
|
207
209
|
)
|
|
208
210
|
|
|
209
211
|
if set(owners) == set(service.agent_addresses):
|
|
210
|
-
ethereum_crypto =
|
|
212
|
+
ethereum_crypto = self.keys_manager.get_crypto_instance(
|
|
211
213
|
service.agent_addresses[0]
|
|
212
214
|
)
|
|
213
215
|
transfer_from_safe(
|
operate/services/manage.py
CHANGED
|
@@ -109,9 +109,10 @@ NUM_LOCAL_AGENT_INSTANCES = 1
|
|
|
109
109
|
class ServiceManager:
|
|
110
110
|
"""Service manager."""
|
|
111
111
|
|
|
112
|
-
def __init__(
|
|
112
|
+
def __init__( # pylint: disable=too-many-arguments
|
|
113
113
|
self,
|
|
114
114
|
path: Path,
|
|
115
|
+
keys_manager: KeysManager,
|
|
115
116
|
wallet_manager: MasterWalletManager,
|
|
116
117
|
funding_manager: FundingManager,
|
|
117
118
|
logger: logging.Logger,
|
|
@@ -126,7 +127,7 @@ class ServiceManager:
|
|
|
126
127
|
:param logger: logging.Logger object.
|
|
127
128
|
"""
|
|
128
129
|
self.path = path
|
|
129
|
-
self.keys_manager =
|
|
130
|
+
self.keys_manager = keys_manager
|
|
130
131
|
self.wallet_manager = wallet_manager
|
|
131
132
|
self.funding_manager = funding_manager
|
|
132
133
|
self.logger = logger
|
|
@@ -812,6 +813,9 @@ class ServiceManager:
|
|
|
812
813
|
on_chain_metadata = self._get_on_chain_metadata(chain_config=chain_config)
|
|
813
814
|
on_chain_hash = on_chain_metadata.get("code_uri", "")[len(IPFS_URI_PREFIX) :]
|
|
814
815
|
on_chain_description = on_chain_metadata.get("description")
|
|
816
|
+
needs_update_agent_addresses = set(chain_data.instances) != set(
|
|
817
|
+
service.agent_addresses
|
|
818
|
+
)
|
|
815
819
|
|
|
816
820
|
current_agent_bond = sftxb.get_agent_bond(
|
|
817
821
|
service_id=chain_data.token, agent_id=target_staking_params["agent_ids"][0]
|
|
@@ -840,6 +844,7 @@ class ServiceManager:
|
|
|
840
844
|
or current_staking_params["staking_token"]
|
|
841
845
|
!= target_staking_params["staking_token"]
|
|
842
846
|
or on_chain_description != service.description
|
|
847
|
+
or needs_update_agent_addresses
|
|
843
848
|
)
|
|
844
849
|
)
|
|
845
850
|
|
|
@@ -1923,7 +1928,9 @@ class ServiceManager:
|
|
|
1923
1928
|
# TODO: remove after staking contract directly starts sending the rewards to master safe
|
|
1924
1929
|
amount_claimed = int(receipt["logs"][0]["data"].hex(), 16)
|
|
1925
1930
|
self.logger.info(f"Claimed amount: {amount_claimed}")
|
|
1926
|
-
ethereum_crypto =
|
|
1931
|
+
ethereum_crypto = self.keys_manager.get_crypto_instance(
|
|
1932
|
+
service.agent_addresses[0]
|
|
1933
|
+
)
|
|
1927
1934
|
transfer_erc20_from_safe(
|
|
1928
1935
|
ledger_api=ledger_api,
|
|
1929
1936
|
crypto=ethereum_crypto,
|
|
@@ -2206,7 +2213,7 @@ class ServiceManager:
|
|
|
2206
2213
|
chain=chain,
|
|
2207
2214
|
)
|
|
2208
2215
|
|
|
2209
|
-
def deploy_service_locally(
|
|
2216
|
+
def deploy_service_locally( # pylint: disable=too-many-arguments
|
|
2210
2217
|
self,
|
|
2211
2218
|
service_config_id: str,
|
|
2212
2219
|
chain: t.Optional[str] = None,
|
|
@@ -2232,11 +2239,15 @@ class ServiceManager:
|
|
|
2232
2239
|
use_kubernetes=use_kubernetes,
|
|
2233
2240
|
force=True,
|
|
2234
2241
|
chain=chain or service.home_chain,
|
|
2235
|
-
|
|
2242
|
+
keys_manager=self.keys_manager,
|
|
2236
2243
|
)
|
|
2237
2244
|
if build_only:
|
|
2238
2245
|
return deployment
|
|
2239
|
-
deployment.start(
|
|
2246
|
+
deployment.start(
|
|
2247
|
+
password=self.wallet_manager.password,
|
|
2248
|
+
use_docker=use_docker,
|
|
2249
|
+
is_aea=service.agent_release["is_aea"],
|
|
2250
|
+
)
|
|
2240
2251
|
return deployment
|
|
2241
2252
|
|
|
2242
2253
|
def stop_service_locally(
|
|
@@ -2256,7 +2267,11 @@ class ServiceManager:
|
|
|
2256
2267
|
service = self.load(service_config_id=service_config_id)
|
|
2257
2268
|
service.remove_latest_healthcheck()
|
|
2258
2269
|
deployment = service.deployment
|
|
2259
|
-
deployment.stop(
|
|
2270
|
+
deployment.stop(
|
|
2271
|
+
use_docker=use_docker,
|
|
2272
|
+
force=force,
|
|
2273
|
+
is_aea=service.agent_release["is_aea"],
|
|
2274
|
+
)
|
|
2260
2275
|
if delete:
|
|
2261
2276
|
deployment.delete()
|
|
2262
2277
|
return deployment
|
operate/services/service.py
CHANGED
|
@@ -79,6 +79,7 @@ from operate.keys import KeysManager
|
|
|
79
79
|
from operate.ledger import get_default_ledger_api, get_default_rpc
|
|
80
80
|
from operate.operate_http.exceptions import NotAllowed
|
|
81
81
|
from operate.operate_types import (
|
|
82
|
+
AgentRelease,
|
|
82
83
|
Chain,
|
|
83
84
|
ChainAmounts,
|
|
84
85
|
ChainConfig,
|
|
@@ -107,7 +108,7 @@ from operate.utils.ssl import create_ssl_certificate
|
|
|
107
108
|
SAFE_CONTRACT_ADDRESS = "safe_contract_address"
|
|
108
109
|
ALL_PARTICIPANTS = "all_participants"
|
|
109
110
|
CONSENSUS_THRESHOLD = "consensus_threshold"
|
|
110
|
-
SERVICE_CONFIG_VERSION =
|
|
111
|
+
SERVICE_CONFIG_VERSION = 9
|
|
111
112
|
SERVICE_CONFIG_PREFIX = "sc-"
|
|
112
113
|
|
|
113
114
|
NON_EXISTENT_MULTISIG = None
|
|
@@ -328,6 +329,8 @@ class HostDeploymentGenerator(BaseDeploymentGenerator):
|
|
|
328
329
|
use_acn: bool = False,
|
|
329
330
|
) -> "HostDeploymentGenerator":
|
|
330
331
|
"""Generate agent and tendermint configurations"""
|
|
332
|
+
self.build_dir.mkdir(exist_ok=True, parents=True)
|
|
333
|
+
(self.build_dir / "agent").mkdir(exist_ok=True, parents=True)
|
|
331
334
|
agent = self.service_builder.generate_agent(agent_n=0)
|
|
332
335
|
agent = {key: f"{value}" for key, value in agent.items()}
|
|
333
336
|
(self.build_dir / "agent.json").write_text(
|
|
@@ -395,7 +398,7 @@ class Deployment(LocalResource):
|
|
|
395
398
|
if source_path.exists():
|
|
396
399
|
shutil.copy(source_path, destination_path)
|
|
397
400
|
|
|
398
|
-
def _build_kubernetes(self,
|
|
401
|
+
def _build_kubernetes(self, keys_manager: KeysManager, force: bool = True) -> None:
|
|
399
402
|
"""Build kubernetes deployment."""
|
|
400
403
|
k8s_build = self.path / DEPLOYMENT_DIR / "abci_build_k8s"
|
|
401
404
|
if k8s_build.exists() and force:
|
|
@@ -407,7 +410,7 @@ class Deployment(LocalResource):
|
|
|
407
410
|
keys_file.write_text(
|
|
408
411
|
json.dumps(
|
|
409
412
|
[
|
|
410
|
-
|
|
413
|
+
keys_manager.get_decrypted(address)
|
|
411
414
|
for address in service.agent_addresses
|
|
412
415
|
],
|
|
413
416
|
indent=4,
|
|
@@ -437,7 +440,7 @@ class Deployment(LocalResource):
|
|
|
437
440
|
|
|
438
441
|
def _build_docker(
|
|
439
442
|
self,
|
|
440
|
-
|
|
443
|
+
keys_manager: KeysManager,
|
|
441
444
|
force: bool = True,
|
|
442
445
|
chain: t.Optional[str] = None,
|
|
443
446
|
) -> None:
|
|
@@ -462,7 +465,7 @@ class Deployment(LocalResource):
|
|
|
462
465
|
keys_file.write_text(
|
|
463
466
|
json.dumps(
|
|
464
467
|
[
|
|
465
|
-
|
|
468
|
+
keys_manager.get_decrypted(address)
|
|
466
469
|
for address in service.agent_addresses
|
|
467
470
|
],
|
|
468
471
|
indent=4,
|
|
@@ -551,7 +554,13 @@ class Deployment(LocalResource):
|
|
|
551
554
|
self.status = DeploymentStatus.BUILT
|
|
552
555
|
self.store()
|
|
553
556
|
|
|
554
|
-
def _build_host(
|
|
557
|
+
def _build_host(
|
|
558
|
+
self,
|
|
559
|
+
keys_manager: KeysManager,
|
|
560
|
+
force: bool = True,
|
|
561
|
+
chain: t.Optional[str] = None,
|
|
562
|
+
with_tm: bool = True,
|
|
563
|
+
) -> None:
|
|
555
564
|
"""Build host depployment."""
|
|
556
565
|
build = self.path / DEPLOYMENT_DIR
|
|
557
566
|
if build.exists() and not force:
|
|
@@ -585,10 +594,7 @@ class Deployment(LocalResource):
|
|
|
585
594
|
keys_file = self.path / DEFAULT_KEYS_FILE
|
|
586
595
|
keys_file.write_text(
|
|
587
596
|
json.dumps(
|
|
588
|
-
[
|
|
589
|
-
KeysManager().get(address).json
|
|
590
|
-
for address in service.agent_addresses
|
|
591
|
-
],
|
|
597
|
+
[keys_manager.get(address).json for address in service.agent_addresses],
|
|
592
598
|
indent=4,
|
|
593
599
|
),
|
|
594
600
|
encoding="utf-8",
|
|
@@ -608,15 +614,21 @@ class Deployment(LocalResource):
|
|
|
608
614
|
consensus_threshold=None,
|
|
609
615
|
)
|
|
610
616
|
|
|
611
|
-
(
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
.generate_config_tendermint()
|
|
618
|
-
|
|
619
|
-
|
|
617
|
+
deployement_generator = HostDeploymentGenerator(
|
|
618
|
+
service_builder=builder,
|
|
619
|
+
build_dir=build.resolve(),
|
|
620
|
+
use_tm_testnet_setup=True,
|
|
621
|
+
)
|
|
622
|
+
if with_tm:
|
|
623
|
+
deployement_generator.generate_config_tendermint()
|
|
624
|
+
|
|
625
|
+
deployement_generator.generate()
|
|
626
|
+
deployement_generator.populate_private_keys()
|
|
627
|
+
|
|
628
|
+
# Add keys
|
|
629
|
+
shutil.copy(
|
|
630
|
+
build / "ethereum_private_key.txt",
|
|
631
|
+
build / "agent" / "ethereum_private_key.txt",
|
|
620
632
|
)
|
|
621
633
|
|
|
622
634
|
except Exception as e:
|
|
@@ -629,7 +641,7 @@ class Deployment(LocalResource):
|
|
|
629
641
|
|
|
630
642
|
def build(
|
|
631
643
|
self,
|
|
632
|
-
|
|
644
|
+
keys_manager: KeysManager,
|
|
633
645
|
use_docker: bool = False,
|
|
634
646
|
use_kubernetes: bool = False,
|
|
635
647
|
force: bool = True,
|
|
@@ -665,9 +677,9 @@ class Deployment(LocalResource):
|
|
|
665
677
|
)
|
|
666
678
|
service.consume_env_variables()
|
|
667
679
|
if use_docker:
|
|
668
|
-
self._build_docker(
|
|
680
|
+
self._build_docker(keys_manager=keys_manager, force=force, chain=chain)
|
|
669
681
|
if use_kubernetes:
|
|
670
|
-
self._build_kubernetes(
|
|
682
|
+
self._build_kubernetes(keys_manager=keys_manager, force=force)
|
|
671
683
|
else:
|
|
672
684
|
ssl_key_path, ssl_cert_path = create_ssl_certificate(
|
|
673
685
|
ssl_dir=service.path / DEPLOYMENT_DIR / "ssl"
|
|
@@ -679,12 +691,23 @@ class Deployment(LocalResource):
|
|
|
679
691
|
}
|
|
680
692
|
)
|
|
681
693
|
service.consume_env_variables()
|
|
682
|
-
|
|
694
|
+
is_aea = service.agent_release["is_aea"]
|
|
695
|
+
self._build_host(
|
|
696
|
+
keys_manager=keys_manager,
|
|
697
|
+
force=force,
|
|
698
|
+
chain=chain,
|
|
699
|
+
with_tm=is_aea,
|
|
700
|
+
)
|
|
683
701
|
|
|
684
702
|
os.environ.clear()
|
|
685
703
|
os.environ.update(original_env)
|
|
686
704
|
|
|
687
|
-
def start(
|
|
705
|
+
def start(
|
|
706
|
+
self,
|
|
707
|
+
password: str,
|
|
708
|
+
use_docker: bool = False,
|
|
709
|
+
is_aea: bool = True,
|
|
710
|
+
) -> None:
|
|
688
711
|
"""Start the service"""
|
|
689
712
|
if self.status != DeploymentStatus.BUILT:
|
|
690
713
|
raise NotAllowed(
|
|
@@ -697,13 +720,15 @@ class Deployment(LocalResource):
|
|
|
697
720
|
try:
|
|
698
721
|
if use_docker:
|
|
699
722
|
run_deployment(
|
|
700
|
-
build_dir=self.path /
|
|
723
|
+
build_dir=self.path / DEPLOYMENT_DIR,
|
|
701
724
|
detach=True,
|
|
702
725
|
project_name=self.path.name,
|
|
703
726
|
)
|
|
704
727
|
else:
|
|
705
728
|
run_host_deployment(
|
|
706
|
-
build_dir=self.path /
|
|
729
|
+
build_dir=self.path / DEPLOYMENT_DIR,
|
|
730
|
+
password=password,
|
|
731
|
+
is_aea=is_aea,
|
|
707
732
|
)
|
|
708
733
|
except Exception:
|
|
709
734
|
self.status = DeploymentStatus.BUILT
|
|
@@ -713,7 +738,12 @@ class Deployment(LocalResource):
|
|
|
713
738
|
self.status = DeploymentStatus.DEPLOYED
|
|
714
739
|
self.store()
|
|
715
740
|
|
|
716
|
-
def stop(
|
|
741
|
+
def stop(
|
|
742
|
+
self,
|
|
743
|
+
use_docker: bool = False,
|
|
744
|
+
force: bool = False,
|
|
745
|
+
is_aea: bool = True,
|
|
746
|
+
) -> None:
|
|
717
747
|
"""Stop the deployment."""
|
|
718
748
|
if self.status != DeploymentStatus.DEPLOYED and not force:
|
|
719
749
|
return
|
|
@@ -723,11 +753,11 @@ class Deployment(LocalResource):
|
|
|
723
753
|
|
|
724
754
|
if use_docker:
|
|
725
755
|
stop_deployment(
|
|
726
|
-
build_dir=self.path /
|
|
756
|
+
build_dir=self.path / DEPLOYMENT_DIR,
|
|
727
757
|
project_name=self.path.name,
|
|
728
758
|
)
|
|
729
759
|
else:
|
|
730
|
-
stop_host_deployment(build_dir=self.path /
|
|
760
|
+
stop_host_deployment(build_dir=self.path / DEPLOYMENT_DIR, is_aea=is_aea)
|
|
731
761
|
|
|
732
762
|
self.status = DeploymentStatus.BUILT
|
|
733
763
|
self.store()
|
|
@@ -744,21 +774,20 @@ class Deployment(LocalResource):
|
|
|
744
774
|
class Service(LocalResource):
|
|
745
775
|
"""Service class."""
|
|
746
776
|
|
|
777
|
+
name: str
|
|
747
778
|
version: int
|
|
748
779
|
service_config_id: str
|
|
780
|
+
path: Path
|
|
781
|
+
package_path: Path
|
|
749
782
|
hash: str
|
|
750
783
|
hash_history: t.Dict[int, str]
|
|
784
|
+
agent_release: AgentRelease
|
|
751
785
|
agent_addresses: t.List[str]
|
|
752
786
|
home_chain: str
|
|
753
787
|
chain_configs: ChainConfigs
|
|
754
788
|
description: str
|
|
755
789
|
env_variables: EnvVariables
|
|
756
790
|
|
|
757
|
-
path: Path
|
|
758
|
-
package_path: Path
|
|
759
|
-
|
|
760
|
-
name: t.Optional[str] = None
|
|
761
|
-
|
|
762
791
|
_helper: t.Optional[ServiceHelper] = None
|
|
763
792
|
_deployment: t.Optional[Deployment] = None
|
|
764
793
|
|
|
@@ -887,6 +916,7 @@ class Service(LocalResource):
|
|
|
887
916
|
path=package_absolute_path.parent,
|
|
888
917
|
package_path=Path(package_absolute_path.name),
|
|
889
918
|
env_variables=service_template["env_variables"],
|
|
919
|
+
agent_release=service_template["agent_release"],
|
|
890
920
|
)
|
|
891
921
|
service.store()
|
|
892
922
|
return service
|
|
@@ -1047,6 +1077,8 @@ class Service(LocalResource):
|
|
|
1047
1077
|
)
|
|
1048
1078
|
self.package_path = Path(package_absolute_path.name)
|
|
1049
1079
|
|
|
1080
|
+
self.agent_release = service_template.get("agent_release", self.agent_release)
|
|
1081
|
+
|
|
1050
1082
|
# env_variables
|
|
1051
1083
|
if partial_update:
|
|
1052
1084
|
for var, attrs in service_template.get("env_variables", {}).items():
|
operate/wallet/master.py
CHANGED
|
@@ -901,14 +901,14 @@ class MasterWalletManager:
|
|
|
901
901
|
return [wallet.json for wallet in self]
|
|
902
902
|
|
|
903
903
|
@property
|
|
904
|
-
def password(self) -> str:
|
|
904
|
+
def password(self) -> t.Optional[str]:
|
|
905
905
|
"""Password string."""
|
|
906
906
|
if self._password is None:
|
|
907
907
|
raise ValueError("Password not set.")
|
|
908
908
|
return self._password
|
|
909
909
|
|
|
910
910
|
@password.setter
|
|
911
|
-
def password(self, value: str) -> None:
|
|
911
|
+
def password(self, value: t.Optional[str]) -> None:
|
|
912
912
|
"""Set password value."""
|
|
913
913
|
self._password = value
|
|
914
914
|
|