fractal-server 2.14.15__py3-none-any.whl → 2.15.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.
- fractal_server/__init__.py +1 -1
- fractal_server/app/models/security.py +2 -2
- fractal_server/app/models/user_settings.py +2 -2
- fractal_server/app/models/v2/dataset.py +3 -3
- fractal_server/app/models/v2/history.py +2 -0
- fractal_server/app/models/v2/job.py +6 -6
- fractal_server/app/models/v2/task.py +12 -8
- fractal_server/app/models/v2/task_group.py +19 -7
- fractal_server/app/models/v2/workflowtask.py +6 -6
- fractal_server/app/routes/admin/v2/task_group_lifecycle.py +2 -5
- fractal_server/app/routes/api/v2/__init__.py +6 -0
- fractal_server/app/routes/api/v2/_aux_functions_tasks.py +22 -0
- fractal_server/app/routes/api/v2/history.py +2 -2
- fractal_server/app/routes/api/v2/pre_submission_checks.py +3 -3
- fractal_server/app/routes/api/v2/task_collection.py +8 -18
- fractal_server/app/routes/api/v2/task_collection_custom.py +2 -2
- fractal_server/app/routes/api/v2/task_collection_pixi.py +219 -0
- fractal_server/app/routes/api/v2/task_group.py +3 -0
- fractal_server/app/routes/api/v2/task_group_lifecycle.py +26 -10
- fractal_server/app/runner/executors/slurm_common/_slurm_config.py +10 -0
- fractal_server/app/runner/executors/slurm_common/base_slurm_runner.py +39 -14
- fractal_server/app/runner/executors/slurm_common/get_slurm_config.py +8 -1
- fractal_server/app/runner/executors/slurm_ssh/runner.py +3 -1
- fractal_server/app/runner/v2/runner.py +2 -2
- fractal_server/app/schemas/v2/__init__.py +1 -1
- fractal_server/app/schemas/v2/dumps.py +1 -1
- fractal_server/app/schemas/v2/task_collection.py +1 -1
- fractal_server/app/schemas/v2/task_group.py +7 -5
- fractal_server/config.py +70 -0
- fractal_server/images/status_tools.py +80 -75
- fractal_server/migrations/versions/791ce783d3d8_add_indices.py +41 -0
- fractal_server/migrations/versions/b1e7f7a1ff71_task_group_for_pixi.py +53 -0
- fractal_server/migrations/versions/b3ffb095f973_json_to_jsonb.py +340 -0
- fractal_server/ssh/_fabric.py +29 -0
- fractal_server/tasks/v2/local/__init__.py +3 -0
- fractal_server/tasks/v2/local/_utils.py +4 -3
- fractal_server/tasks/v2/local/collect.py +26 -30
- fractal_server/tasks/v2/local/collect_pixi.py +252 -0
- fractal_server/tasks/v2/local/deactivate.py +39 -46
- fractal_server/tasks/v2/local/deactivate_pixi.py +98 -0
- fractal_server/tasks/v2/local/reactivate.py +12 -23
- fractal_server/tasks/v2/local/reactivate_pixi.py +184 -0
- fractal_server/tasks/v2/ssh/__init__.py +3 -0
- fractal_server/tasks/v2/ssh/_utils.py +50 -9
- fractal_server/tasks/v2/ssh/collect.py +46 -56
- fractal_server/tasks/v2/ssh/collect_pixi.py +315 -0
- fractal_server/tasks/v2/ssh/deactivate.py +54 -67
- fractal_server/tasks/v2/ssh/deactivate_pixi.py +122 -0
- fractal_server/tasks/v2/ssh/reactivate.py +25 -38
- fractal_server/tasks/v2/ssh/reactivate_pixi.py +233 -0
- fractal_server/tasks/v2/templates/pixi_1_extract.sh +40 -0
- fractal_server/tasks/v2/templates/pixi_2_install.sh +52 -0
- fractal_server/tasks/v2/templates/pixi_3_post_install.sh +76 -0
- fractal_server/tasks/v2/utils_background.py +50 -8
- fractal_server/tasks/v2/utils_pixi.py +38 -0
- fractal_server/tasks/v2/utils_templates.py +14 -1
- {fractal_server-2.14.15.dist-info → fractal_server-2.15.0.dist-info}/METADATA +4 -4
- {fractal_server-2.14.15.dist-info → fractal_server-2.15.0.dist-info}/RECORD +61 -47
- {fractal_server-2.14.15.dist-info → fractal_server-2.15.0.dist-info}/LICENSE +0 -0
- {fractal_server-2.14.15.dist-info → fractal_server-2.15.0.dist-info}/WHEEL +0 -0
- {fractal_server-2.14.15.dist-info → fractal_server-2.15.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,315 @@
|
|
1
|
+
import time
|
2
|
+
from pathlib import Path
|
3
|
+
from tempfile import TemporaryDirectory
|
4
|
+
|
5
|
+
from ..utils_background import fail_and_cleanup
|
6
|
+
from ..utils_background import get_activity_and_task_group
|
7
|
+
from ..utils_background import prepare_tasks_metadata
|
8
|
+
from ..utils_database import create_db_tasks_and_update_task_group_sync
|
9
|
+
from ..utils_pixi import parse_collect_stdout
|
10
|
+
from ..utils_pixi import SOURCE_DIR_NAME
|
11
|
+
from ._utils import check_ssh_or_fail_and_cleanup
|
12
|
+
from fractal_server.app.db import get_sync_db
|
13
|
+
from fractal_server.app.schemas.v2 import FractalUploadedFile
|
14
|
+
from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
|
15
|
+
from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
|
16
|
+
from fractal_server.app.schemas.v2.manifest import ManifestV2
|
17
|
+
from fractal_server.config import get_settings
|
18
|
+
from fractal_server.logger import reset_logger_handlers
|
19
|
+
from fractal_server.logger import set_logger
|
20
|
+
from fractal_server.ssh._fabric import SingleUseFractalSSH
|
21
|
+
from fractal_server.ssh._fabric import SSHConfig
|
22
|
+
from fractal_server.syringe import Inject
|
23
|
+
from fractal_server.tasks.v2.ssh._utils import _customize_and_run_template
|
24
|
+
from fractal_server.tasks.v2.utils_background import add_commit_refresh
|
25
|
+
from fractal_server.tasks.v2.utils_background import get_current_log
|
26
|
+
from fractal_server.tasks.v2.utils_templates import SCRIPTS_SUBFOLDER
|
27
|
+
from fractal_server.utils import get_timestamp
|
28
|
+
|
29
|
+
|
30
|
+
def collect_ssh_pixi(
|
31
|
+
*,
|
32
|
+
task_group_id: int,
|
33
|
+
task_group_activity_id: int,
|
34
|
+
ssh_config: SSHConfig,
|
35
|
+
tasks_base_dir: str,
|
36
|
+
tar_gz_file: FractalUploadedFile,
|
37
|
+
) -> None:
|
38
|
+
"""
|
39
|
+
Collect a task package over SSH
|
40
|
+
|
41
|
+
This function runs as a background task, therefore exceptions must be
|
42
|
+
handled.
|
43
|
+
|
44
|
+
NOTE: since this function is sync, it runs within a thread - due to
|
45
|
+
starlette/fastapi handling of background tasks (see
|
46
|
+
https://github.com/encode/starlette/blob/master/starlette/background.py).
|
47
|
+
|
48
|
+
|
49
|
+
Arguments:
|
50
|
+
task_group_id:
|
51
|
+
task_group_activity_id:
|
52
|
+
ssh_config:
|
53
|
+
tasks_base_dir:
|
54
|
+
Only used as a `safe_root` in `remove_dir`, and typically set to
|
55
|
+
`user_settings.ssh_tasks_dir`.
|
56
|
+
tar_gz_file:
|
57
|
+
"""
|
58
|
+
|
59
|
+
LOGGER_NAME = f"{__name__}.ID{task_group_activity_id}"
|
60
|
+
|
61
|
+
# Work within a temporary folder, where also logs will be placed
|
62
|
+
with TemporaryDirectory() as tmpdir:
|
63
|
+
log_file_path = Path(tmpdir) / "log"
|
64
|
+
logger = set_logger(
|
65
|
+
logger_name=LOGGER_NAME,
|
66
|
+
log_file_path=log_file_path,
|
67
|
+
)
|
68
|
+
logger.info("START")
|
69
|
+
with next(get_sync_db()) as db:
|
70
|
+
db_objects_ok, task_group, activity = get_activity_and_task_group(
|
71
|
+
task_group_activity_id=task_group_activity_id,
|
72
|
+
task_group_id=task_group_id,
|
73
|
+
db=db,
|
74
|
+
logger_name=LOGGER_NAME,
|
75
|
+
)
|
76
|
+
if not db_objects_ok:
|
77
|
+
return
|
78
|
+
|
79
|
+
with SingleUseFractalSSH(
|
80
|
+
ssh_config=ssh_config,
|
81
|
+
logger_name=LOGGER_NAME,
|
82
|
+
) as fractal_ssh:
|
83
|
+
|
84
|
+
try:
|
85
|
+
# Check SSH connection
|
86
|
+
ssh_ok = check_ssh_or_fail_and_cleanup(
|
87
|
+
fractal_ssh=fractal_ssh,
|
88
|
+
task_group=task_group,
|
89
|
+
task_group_activity=activity,
|
90
|
+
logger_name=LOGGER_NAME,
|
91
|
+
log_file_path=log_file_path,
|
92
|
+
db=db,
|
93
|
+
)
|
94
|
+
if not ssh_ok:
|
95
|
+
return
|
96
|
+
|
97
|
+
# Check that the (remote) task_group path does not exist
|
98
|
+
if fractal_ssh.remote_exists(task_group.path):
|
99
|
+
error_msg = f"{task_group.path} already exists."
|
100
|
+
logger.error(error_msg)
|
101
|
+
fail_and_cleanup(
|
102
|
+
task_group=task_group,
|
103
|
+
task_group_activity=activity,
|
104
|
+
logger_name=LOGGER_NAME,
|
105
|
+
log_file_path=log_file_path,
|
106
|
+
exception=FileExistsError(error_msg),
|
107
|
+
db=db,
|
108
|
+
)
|
109
|
+
return
|
110
|
+
|
111
|
+
# Create remote `task_group.path` and `script_dir_remote`
|
112
|
+
# folders
|
113
|
+
script_dir_remote = Path(
|
114
|
+
task_group.path, SCRIPTS_SUBFOLDER
|
115
|
+
).as_posix()
|
116
|
+
fractal_ssh.mkdir(folder=task_group.path, parents=True)
|
117
|
+
fractal_ssh.mkdir(folder=script_dir_remote, parents=True)
|
118
|
+
|
119
|
+
# Write tar.gz file locally and send it to remote path,
|
120
|
+
# and set task_group.archive_path
|
121
|
+
tar_gz_filename = tar_gz_file.filename
|
122
|
+
archive_path = (
|
123
|
+
Path(task_group.path) / tar_gz_filename
|
124
|
+
).as_posix()
|
125
|
+
tmp_archive_path = Path(tmpdir, tar_gz_filename).as_posix()
|
126
|
+
logger.info(f"Write tar.gz file into {tmp_archive_path}")
|
127
|
+
with open(tmp_archive_path, "wb") as f:
|
128
|
+
f.write(tar_gz_file.contents)
|
129
|
+
fractal_ssh.send_file(
|
130
|
+
local=tmp_archive_path,
|
131
|
+
remote=archive_path,
|
132
|
+
)
|
133
|
+
task_group.archive_path = archive_path
|
134
|
+
task_group = add_commit_refresh(obj=task_group, db=db)
|
135
|
+
|
136
|
+
settings = Inject(get_settings)
|
137
|
+
replacements = {
|
138
|
+
(
|
139
|
+
"__PIXI_HOME__",
|
140
|
+
settings.pixi.versions[task_group.pixi_version],
|
141
|
+
),
|
142
|
+
("__PACKAGE_DIR__", task_group.path),
|
143
|
+
("__TAR_GZ_PATH__", task_group.archive_path),
|
144
|
+
(
|
145
|
+
"__IMPORT_PACKAGE_NAME__",
|
146
|
+
task_group.pkg_name.replace("-", "_"),
|
147
|
+
),
|
148
|
+
("__SOURCE_DIR_NAME__", SOURCE_DIR_NAME),
|
149
|
+
("__FROZEN_OPTION__", ""),
|
150
|
+
(
|
151
|
+
"__TOKIO_WORKER_THREADS__",
|
152
|
+
str(settings.pixi.TOKIO_WORKER_THREADS),
|
153
|
+
),
|
154
|
+
(
|
155
|
+
"__PIXI_CONCURRENT_SOLVES__",
|
156
|
+
str(settings.pixi.PIXI_CONCURRENT_SOLVES),
|
157
|
+
),
|
158
|
+
(
|
159
|
+
"__PIXI_CONCURRENT_DOWNLOADS__",
|
160
|
+
str(settings.pixi.PIXI_CONCURRENT_DOWNLOADS),
|
161
|
+
),
|
162
|
+
}
|
163
|
+
|
164
|
+
logger.info("installing - START")
|
165
|
+
|
166
|
+
# Set status to ONGOING and refresh logs
|
167
|
+
activity.status = TaskGroupActivityStatusV2.ONGOING
|
168
|
+
activity.log = get_current_log(log_file_path)
|
169
|
+
activity = add_commit_refresh(obj=activity, db=db)
|
170
|
+
|
171
|
+
common_args = dict(
|
172
|
+
script_dir_local=(
|
173
|
+
Path(tmpdir, SCRIPTS_SUBFOLDER)
|
174
|
+
).as_posix(),
|
175
|
+
script_dir_remote=script_dir_remote,
|
176
|
+
prefix=(
|
177
|
+
f"{int(time.time())}_"
|
178
|
+
f"{TaskGroupActivityActionV2.COLLECT}"
|
179
|
+
),
|
180
|
+
logger_name=LOGGER_NAME,
|
181
|
+
fractal_ssh=fractal_ssh,
|
182
|
+
)
|
183
|
+
|
184
|
+
# Run the three pixi-related scripts
|
185
|
+
stdout = _customize_and_run_template(
|
186
|
+
template_filename="pixi_1_extract.sh",
|
187
|
+
replacements=replacements,
|
188
|
+
**common_args,
|
189
|
+
)
|
190
|
+
logger.debug(f"STDOUT: {stdout}")
|
191
|
+
activity.log = get_current_log(log_file_path)
|
192
|
+
activity = add_commit_refresh(obj=activity, db=db)
|
193
|
+
|
194
|
+
stdout = _customize_and_run_template(
|
195
|
+
template_filename="pixi_2_install.sh",
|
196
|
+
replacements=replacements,
|
197
|
+
**common_args,
|
198
|
+
)
|
199
|
+
logger.debug(f"STDOUT: {stdout}")
|
200
|
+
activity.log = get_current_log(log_file_path)
|
201
|
+
activity = add_commit_refresh(obj=activity, db=db)
|
202
|
+
|
203
|
+
stdout = _customize_and_run_template(
|
204
|
+
template_filename="pixi_3_post_install.sh",
|
205
|
+
replacements=replacements,
|
206
|
+
**common_args,
|
207
|
+
)
|
208
|
+
logger.debug(f"STDOUT: {stdout}")
|
209
|
+
activity.log = get_current_log(log_file_path)
|
210
|
+
activity = add_commit_refresh(obj=activity, db=db)
|
211
|
+
|
212
|
+
# Parse stdout
|
213
|
+
parsed_output = parse_collect_stdout(stdout)
|
214
|
+
package_root_remote = parsed_output["package_root"]
|
215
|
+
venv_size = parsed_output["venv_size"]
|
216
|
+
venv_file_number = parsed_output["venv_file_number"]
|
217
|
+
project_python_wrapper = parsed_output[
|
218
|
+
"project_python_wrapper"
|
219
|
+
]
|
220
|
+
|
221
|
+
source_dir = Path(
|
222
|
+
task_group.path, SOURCE_DIR_NAME
|
223
|
+
).as_posix()
|
224
|
+
fractal_ssh.run_command(cmd=f"chmod 755 {source_dir} -R")
|
225
|
+
|
226
|
+
# Read and validate remote manifest file
|
227
|
+
manifest_path_remote = (
|
228
|
+
f"{package_root_remote}/__FRACTAL_MANIFEST__.json"
|
229
|
+
)
|
230
|
+
pkg_manifest_dict = fractal_ssh.read_remote_json_file(
|
231
|
+
manifest_path_remote
|
232
|
+
)
|
233
|
+
logger.info(f"Loaded {manifest_path_remote=}")
|
234
|
+
pkg_manifest = ManifestV2(**pkg_manifest_dict)
|
235
|
+
logger.info("Manifest is a valid ManifestV2")
|
236
|
+
|
237
|
+
logger.info("_prepare_tasks_metadata - start")
|
238
|
+
task_list = prepare_tasks_metadata(
|
239
|
+
package_manifest=pkg_manifest,
|
240
|
+
package_version=task_group.version,
|
241
|
+
package_root=Path(package_root_remote),
|
242
|
+
project_python_wrapper=Path(project_python_wrapper),
|
243
|
+
)
|
244
|
+
logger.info("_prepare_tasks_metadata - end")
|
245
|
+
|
246
|
+
logger.info(
|
247
|
+
"create_db_tasks_and_update_task_group - " "start"
|
248
|
+
)
|
249
|
+
create_db_tasks_and_update_task_group_sync(
|
250
|
+
task_list=task_list,
|
251
|
+
task_group_id=task_group.id,
|
252
|
+
db=db,
|
253
|
+
)
|
254
|
+
logger.info("create_db_tasks_and_update_task_group - end")
|
255
|
+
|
256
|
+
# NOTE: see issue 2626 about whether to keep `pixi.lock`
|
257
|
+
# files in the database
|
258
|
+
remote_pixi_lock_file = Path(
|
259
|
+
task_group.path,
|
260
|
+
SOURCE_DIR_NAME,
|
261
|
+
"pixi.lock",
|
262
|
+
).as_posix()
|
263
|
+
pixi_lock_contents = fractal_ssh.read_remote_text_file(
|
264
|
+
remote_pixi_lock_file
|
265
|
+
)
|
266
|
+
|
267
|
+
# Update task_group data
|
268
|
+
logger.info(
|
269
|
+
"Add env_info, venv_size and venv_file_number "
|
270
|
+
"to TaskGroupV2 - start"
|
271
|
+
)
|
272
|
+
task_group.env_info = pixi_lock_contents
|
273
|
+
task_group.venv_size_in_kB = int(venv_size)
|
274
|
+
task_group.venv_file_number = int(venv_file_number)
|
275
|
+
task_group = add_commit_refresh(obj=task_group, db=db)
|
276
|
+
logger.info(
|
277
|
+
"Add env_info, venv_size and venv_file_number "
|
278
|
+
"to TaskGroupV2 - end"
|
279
|
+
)
|
280
|
+
|
281
|
+
# Finalize (write metadata to DB)
|
282
|
+
logger.info("finalising - START")
|
283
|
+
activity.status = TaskGroupActivityStatusV2.OK
|
284
|
+
activity.timestamp_ended = get_timestamp()
|
285
|
+
activity = add_commit_refresh(obj=activity, db=db)
|
286
|
+
logger.info("finalising - END")
|
287
|
+
logger.info("END")
|
288
|
+
reset_logger_handlers(logger)
|
289
|
+
|
290
|
+
except Exception as collection_e:
|
291
|
+
# Delete corrupted package dir
|
292
|
+
try:
|
293
|
+
logger.info(
|
294
|
+
f"Now delete remote folder {task_group.path}"
|
295
|
+
)
|
296
|
+
fractal_ssh.remove_folder(
|
297
|
+
folder=task_group.path,
|
298
|
+
safe_root=tasks_base_dir,
|
299
|
+
)
|
300
|
+
logger.info(
|
301
|
+
f"Deleted remoted folder {task_group.path}"
|
302
|
+
)
|
303
|
+
except Exception as e_rm:
|
304
|
+
logger.error(
|
305
|
+
"Removing folder failed. "
|
306
|
+
f"Original error: {str(e_rm)}"
|
307
|
+
)
|
308
|
+
fail_and_cleanup(
|
309
|
+
task_group=task_group,
|
310
|
+
task_group_activity=activity,
|
311
|
+
log_file_path=log_file_path,
|
312
|
+
logger_name=LOGGER_NAME,
|
313
|
+
exception=collection_e,
|
314
|
+
db=db,
|
315
|
+
)
|
@@ -1,16 +1,15 @@
|
|
1
|
-
import logging
|
2
1
|
import time
|
3
2
|
from pathlib import Path
|
4
3
|
from tempfile import TemporaryDirectory
|
5
4
|
|
6
5
|
from ..utils_background import add_commit_refresh
|
7
6
|
from ..utils_background import fail_and_cleanup
|
7
|
+
from ..utils_background import get_activity_and_task_group
|
8
8
|
from ..utils_templates import get_collection_replacements
|
9
9
|
from ._utils import _copy_wheel_file_ssh
|
10
10
|
from ._utils import _customize_and_run_template
|
11
|
+
from ._utils import check_ssh_or_fail_and_cleanup
|
11
12
|
from fractal_server.app.db import get_sync_db
|
12
|
-
from fractal_server.app.models.v2 import TaskGroupActivityV2
|
13
|
-
from fractal_server.app.models.v2 import TaskGroupV2
|
14
13
|
from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
|
15
14
|
from fractal_server.app.schemas.v2 import TaskGroupV2OriginEnum
|
16
15
|
from fractal_server.app.schemas.v2.task_group import TaskGroupActivityStatusV2
|
@@ -55,46 +54,33 @@ def deactivate_ssh(
|
|
55
54
|
logger_name=LOGGER_NAME,
|
56
55
|
log_file_path=log_file_path,
|
57
56
|
)
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
f"{task_group=}\n{activity=}. Exit."
|
74
|
-
)
|
75
|
-
return
|
76
|
-
|
77
|
-
# Log some info
|
78
|
-
logger.debug("START")
|
79
|
-
for key, value in task_group.model_dump().items():
|
80
|
-
logger.debug(f"task_group.{key}: {value}")
|
81
|
-
|
82
|
-
# Check that SSH connection works
|
57
|
+
logger.debug("START")
|
58
|
+
with next(get_sync_db()) as db:
|
59
|
+
db_objects_ok, task_group, activity = get_activity_and_task_group(
|
60
|
+
task_group_activity_id=task_group_activity_id,
|
61
|
+
task_group_id=task_group_id,
|
62
|
+
db=db,
|
63
|
+
logger_name=LOGGER_NAME,
|
64
|
+
)
|
65
|
+
if not db_objects_ok:
|
66
|
+
return
|
67
|
+
|
68
|
+
with SingleUseFractalSSH(
|
69
|
+
ssh_config=ssh_config,
|
70
|
+
logger_name=LOGGER_NAME,
|
71
|
+
) as fractal_ssh:
|
83
72
|
try:
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
fail_and_cleanup(
|
73
|
+
# Check SSH connection
|
74
|
+
ssh_ok = check_ssh_or_fail_and_cleanup(
|
75
|
+
fractal_ssh=fractal_ssh,
|
88
76
|
task_group=task_group,
|
89
77
|
task_group_activity=activity,
|
90
78
|
logger_name=LOGGER_NAME,
|
91
79
|
log_file_path=log_file_path,
|
92
|
-
exception=e,
|
93
80
|
db=db,
|
94
81
|
)
|
95
|
-
|
96
|
-
|
97
|
-
try:
|
82
|
+
if not ssh_ok:
|
83
|
+
return
|
98
84
|
|
99
85
|
# Check that the (local) task_group venv_path does exist
|
100
86
|
if not fractal_ssh.remote_exists(task_group.venv_path):
|
@@ -113,10 +99,10 @@ def deactivate_ssh(
|
|
113
99
|
activity.status = TaskGroupActivityStatusV2.ONGOING
|
114
100
|
activity = add_commit_refresh(obj=activity, db=db)
|
115
101
|
|
116
|
-
if task_group.
|
102
|
+
if task_group.env_info is None:
|
117
103
|
logger.warning(
|
118
104
|
"Recreate pip-freeze information, since "
|
119
|
-
f"{task_group.
|
105
|
+
f"{task_group.env_info=}. NOTE: this should "
|
120
106
|
"only happen for task groups created before 2.9.0."
|
121
107
|
)
|
122
108
|
|
@@ -161,7 +147,7 @@ def deactivate_ssh(
|
|
161
147
|
)
|
162
148
|
activity.log = get_current_log(log_file_path)
|
163
149
|
activity = add_commit_refresh(obj=activity, db=db)
|
164
|
-
task_group.
|
150
|
+
task_group.env_info = pip_freeze_stdout
|
165
151
|
task_group = add_commit_refresh(obj=task_group, db=db)
|
166
152
|
logger.info(
|
167
153
|
"Add pip freeze stdout to TaskGroupV2 - end"
|
@@ -174,18 +160,19 @@ def deactivate_ssh(
|
|
174
160
|
f"Handle specific cases for {task_group.origin=}."
|
175
161
|
)
|
176
162
|
|
177
|
-
# Blocking situation: `
|
178
|
-
# to a missing path
|
163
|
+
# Blocking situation: `archive_path` is not set or
|
164
|
+
# points to a missing path
|
179
165
|
if (
|
180
|
-
task_group.
|
166
|
+
task_group.archive_path is None
|
181
167
|
or not fractal_ssh.remote_exists(
|
182
|
-
task_group.
|
168
|
+
task_group.archive_path
|
183
169
|
)
|
184
170
|
):
|
185
171
|
error_msg = (
|
186
172
|
"Invalid wheel path for task group with "
|
187
|
-
f"{task_group_id=}.
|
188
|
-
"is unset or
|
173
|
+
f"{task_group_id=}. "
|
174
|
+
f"{task_group.archive_path=} is unset or "
|
175
|
+
"does not exist."
|
189
176
|
)
|
190
177
|
logger.error(error_msg)
|
191
178
|
fail_and_cleanup(
|
@@ -198,58 +185,58 @@ def deactivate_ssh(
|
|
198
185
|
)
|
199
186
|
return
|
200
187
|
|
201
|
-
# Recoverable situation: `
|
188
|
+
# Recoverable situation: `archive_path` was not yet
|
202
189
|
# copied over to the correct server-side folder
|
203
|
-
|
204
|
-
task_group.
|
190
|
+
archive_path_parent_dir = Path(
|
191
|
+
task_group.archive_path
|
205
192
|
).parent
|
206
|
-
if
|
193
|
+
if archive_path_parent_dir != Path(task_group.path):
|
207
194
|
logger.warning(
|
208
|
-
f"{
|
209
|
-
f"from {task_group.path}.
|
210
|
-
"only happen for task
|
211
|
-
"2.9.0."
|
195
|
+
f"{archive_path_parent_dir.as_posix()} "
|
196
|
+
f"differs from {task_group.path}. "
|
197
|
+
"NOTE: this should only happen for task "
|
198
|
+
"groups created before 2.9.0."
|
212
199
|
)
|
213
200
|
|
214
201
|
if (
|
215
|
-
task_group.
|
216
|
-
not in task_group.
|
202
|
+
task_group.archive_path
|
203
|
+
not in task_group.env_info
|
217
204
|
):
|
218
205
|
raise ValueError(
|
219
|
-
f"Cannot find {task_group.
|
220
|
-
"pip-freeze data. Exit."
|
206
|
+
f"Cannot find {task_group.archive_path=} "
|
207
|
+
"in pip-freeze data. Exit."
|
221
208
|
)
|
222
209
|
|
223
210
|
logger.info(
|
224
211
|
f"Now copy wheel file into {task_group.path}."
|
225
212
|
)
|
226
|
-
|
213
|
+
new_archive_path = _copy_wheel_file_ssh(
|
227
214
|
task_group=task_group,
|
228
215
|
fractal_ssh=fractal_ssh,
|
229
216
|
logger_name=LOGGER_NAME,
|
230
217
|
)
|
231
218
|
logger.info(
|
232
|
-
f"Copied wheel file to {
|
219
|
+
f"Copied wheel file to {new_archive_path}."
|
233
220
|
)
|
234
221
|
|
235
|
-
task_group.
|
236
|
-
new_pip_freeze = task_group.
|
237
|
-
task_group.
|
238
|
-
|
222
|
+
task_group.archive_path = new_archive_path
|
223
|
+
new_pip_freeze = task_group.env_info.replace(
|
224
|
+
task_group.archive_path,
|
225
|
+
new_archive_path,
|
239
226
|
)
|
240
|
-
task_group.
|
227
|
+
task_group.env_info = new_pip_freeze
|
241
228
|
task_group = add_commit_refresh(
|
242
229
|
obj=task_group, db=db
|
243
230
|
)
|
244
231
|
logger.info(
|
245
|
-
"Updated `
|
232
|
+
"Updated `archive_path` and `env_info` "
|
246
233
|
"task-group attributes."
|
247
234
|
)
|
248
235
|
|
249
|
-
# Fail if `
|
236
|
+
# Fail if `env_info` includes "github", see
|
250
237
|
# https://github.com/fractal-analytics-platform/fractal-server/issues/2142
|
251
238
|
for forbidden_string in FORBIDDEN_DEPENDENCY_STRINGS:
|
252
|
-
if forbidden_string in task_group.
|
239
|
+
if forbidden_string in task_group.env_info:
|
253
240
|
raise ValueError(
|
254
241
|
"Deactivation and reactivation of task "
|
255
242
|
f"packages with direct {forbidden_string} "
|
@@ -0,0 +1,122 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
from tempfile import TemporaryDirectory
|
3
|
+
|
4
|
+
from ..utils_background import add_commit_refresh
|
5
|
+
from ..utils_background import fail_and_cleanup
|
6
|
+
from ..utils_background import get_activity_and_task_group
|
7
|
+
from ..utils_pixi import SOURCE_DIR_NAME
|
8
|
+
from ._utils import check_ssh_or_fail_and_cleanup
|
9
|
+
from fractal_server.app.db import get_sync_db
|
10
|
+
from fractal_server.app.schemas.v2.task_group import TaskGroupActivityStatusV2
|
11
|
+
from fractal_server.logger import reset_logger_handlers
|
12
|
+
from fractal_server.logger import set_logger
|
13
|
+
from fractal_server.ssh._fabric import SingleUseFractalSSH
|
14
|
+
from fractal_server.ssh._fabric import SSHConfig
|
15
|
+
from fractal_server.tasks.utils import get_log_path
|
16
|
+
from fractal_server.tasks.v2.utils_background import get_current_log
|
17
|
+
from fractal_server.utils import get_timestamp
|
18
|
+
|
19
|
+
|
20
|
+
def deactivate_ssh_pixi(
|
21
|
+
*,
|
22
|
+
task_group_activity_id: int,
|
23
|
+
task_group_id: int,
|
24
|
+
ssh_config: SSHConfig,
|
25
|
+
tasks_base_dir: str,
|
26
|
+
) -> None:
|
27
|
+
"""
|
28
|
+
Deactivate a pixi task group venv.
|
29
|
+
|
30
|
+
This function is run as a background task, therefore exceptions must be
|
31
|
+
handled.
|
32
|
+
|
33
|
+
Arguments:
|
34
|
+
task_group_id:
|
35
|
+
task_group_activity_id:
|
36
|
+
ssh_config:
|
37
|
+
tasks_base_dir:
|
38
|
+
Only used as a `safe_root` in `remove_dir`, and typically set to
|
39
|
+
`user_settings.ssh_tasks_dir`.
|
40
|
+
"""
|
41
|
+
|
42
|
+
LOGGER_NAME = f"{__name__}.ID{task_group_activity_id}"
|
43
|
+
|
44
|
+
with TemporaryDirectory() as tmpdir:
|
45
|
+
log_file_path = get_log_path(Path(tmpdir))
|
46
|
+
logger = set_logger(
|
47
|
+
logger_name=LOGGER_NAME,
|
48
|
+
log_file_path=log_file_path,
|
49
|
+
)
|
50
|
+
logger.debug("START")
|
51
|
+
with next(get_sync_db()) as db:
|
52
|
+
db_objects_ok, task_group, activity = get_activity_and_task_group(
|
53
|
+
task_group_activity_id=task_group_activity_id,
|
54
|
+
task_group_id=task_group_id,
|
55
|
+
db=db,
|
56
|
+
logger_name=LOGGER_NAME,
|
57
|
+
)
|
58
|
+
if not db_objects_ok:
|
59
|
+
return
|
60
|
+
|
61
|
+
with SingleUseFractalSSH(
|
62
|
+
ssh_config=ssh_config,
|
63
|
+
logger_name=LOGGER_NAME,
|
64
|
+
) as fractal_ssh:
|
65
|
+
try:
|
66
|
+
# Check SSH connection
|
67
|
+
ssh_ok = check_ssh_or_fail_and_cleanup(
|
68
|
+
fractal_ssh=fractal_ssh,
|
69
|
+
task_group=task_group,
|
70
|
+
task_group_activity=activity,
|
71
|
+
logger_name=LOGGER_NAME,
|
72
|
+
log_file_path=log_file_path,
|
73
|
+
db=db,
|
74
|
+
)
|
75
|
+
if not ssh_ok:
|
76
|
+
return
|
77
|
+
|
78
|
+
# Check that the (remote) task_group venv_path does exist
|
79
|
+
source_dir = Path(
|
80
|
+
task_group.path, SOURCE_DIR_NAME
|
81
|
+
).as_posix()
|
82
|
+
if not fractal_ssh.remote_exists(source_dir):
|
83
|
+
error_msg = f"{source_dir} does not exist."
|
84
|
+
logger.error(error_msg)
|
85
|
+
fail_and_cleanup(
|
86
|
+
task_group=task_group,
|
87
|
+
task_group_activity=activity,
|
88
|
+
logger_name=LOGGER_NAME,
|
89
|
+
log_file_path=log_file_path,
|
90
|
+
exception=FileNotFoundError(error_msg),
|
91
|
+
db=db,
|
92
|
+
)
|
93
|
+
return
|
94
|
+
|
95
|
+
# Actually mark the task group as non-active
|
96
|
+
logger.info("Now setting `active=False`.")
|
97
|
+
task_group.active = False
|
98
|
+
task_group = add_commit_refresh(obj=task_group, db=db)
|
99
|
+
|
100
|
+
# Proceed with deactivation
|
101
|
+
logger.info(f"Now removing {source_dir}.")
|
102
|
+
fractal_ssh.remove_folder(
|
103
|
+
folder=source_dir,
|
104
|
+
safe_root=tasks_base_dir,
|
105
|
+
)
|
106
|
+
logger.info(f"All good, {source_dir} removed.")
|
107
|
+
activity.status = TaskGroupActivityStatusV2.OK
|
108
|
+
activity.log = get_current_log(log_file_path)
|
109
|
+
activity.timestamp_ended = get_timestamp()
|
110
|
+
activity = add_commit_refresh(obj=activity, db=db)
|
111
|
+
|
112
|
+
reset_logger_handlers(logger)
|
113
|
+
|
114
|
+
except Exception as e:
|
115
|
+
fail_and_cleanup(
|
116
|
+
task_group=task_group,
|
117
|
+
task_group_activity=activity,
|
118
|
+
logger_name=LOGGER_NAME,
|
119
|
+
log_file_path=log_file_path,
|
120
|
+
exception=e,
|
121
|
+
db=db,
|
122
|
+
)
|