fractal-server 2.15.0a0__py3-none-any.whl → 2.15.0a2__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.
Files changed (29) hide show
  1. fractal_server/__init__.py +1 -1
  2. fractal_server/app/routes/api/v2/task_collection_pixi.py +1 -1
  3. fractal_server/app/routes/api/v2/task_group_lifecycle.py +18 -4
  4. fractal_server/ssh/_fabric.py +26 -0
  5. fractal_server/tasks/v2/local/__init__.py +1 -0
  6. fractal_server/tasks/v2/local/collect.py +9 -12
  7. fractal_server/tasks/v2/local/collect_pixi.py +53 -41
  8. fractal_server/tasks/v2/local/deactivate.py +7 -14
  9. fractal_server/tasks/v2/local/deactivate_pixi.py +8 -16
  10. fractal_server/tasks/v2/local/reactivate.py +8 -15
  11. fractal_server/tasks/v2/local/reactivate_pixi.py +166 -0
  12. fractal_server/tasks/v2/ssh/__init__.py +2 -0
  13. fractal_server/tasks/v2/ssh/collect.py +7 -13
  14. fractal_server/tasks/v2/ssh/collect_pixi.py +44 -34
  15. fractal_server/tasks/v2/ssh/deactivate.py +7 -14
  16. fractal_server/tasks/v2/ssh/deactivate_pixi.py +128 -0
  17. fractal_server/tasks/v2/ssh/reactivate.py +7 -14
  18. fractal_server/tasks/v2/ssh/reactivate_pixi.py +108 -0
  19. fractal_server/tasks/v2/templates/pixi_1_extract.sh +40 -0
  20. fractal_server/tasks/v2/templates/pixi_2_install.sh +48 -0
  21. fractal_server/tasks/v2/templates/pixi_3_post_install.sh +80 -0
  22. fractal_server/tasks/v2/utils_background.py +35 -28
  23. fractal_server/tasks/v2/utils_pixi.py +2 -0
  24. {fractal_server-2.15.0a0.dist-info → fractal_server-2.15.0a2.dist-info}/METADATA +1 -1
  25. {fractal_server-2.15.0a0.dist-info → fractal_server-2.15.0a2.dist-info}/RECORD +28 -23
  26. fractal_server/tasks/v2/templates/pixi_1_collect.sh +0 -70
  27. {fractal_server-2.15.0a0.dist-info → fractal_server-2.15.0a2.dist-info}/LICENSE +0 -0
  28. {fractal_server-2.15.0a0.dist-info → fractal_server-2.15.0a2.dist-info}/WHEEL +0 -0
  29. {fractal_server-2.15.0a0.dist-info → fractal_server-2.15.0a2.dist-info}/entry_points.txt +0 -0
@@ -1 +1 @@
1
- __VERSION__ = "2.15.0a0"
1
+ __VERSION__ = "2.15.0a2"
@@ -107,7 +107,7 @@ async def collect_task_pixi(
107
107
  raise HTTPException(
108
108
  status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
109
109
  detail=(
110
- f"Pixi version {pixi_version} is not available. Available"
110
+ f"Pixi version {pixi_version} is not available. Available "
111
111
  f"versions: {list(settings.pixi.versions.keys())}"
112
112
  ),
113
113
  )
@@ -27,8 +27,11 @@ from fractal_server.syringe import Inject
27
27
  from fractal_server.tasks.v2.local import deactivate_local
28
28
  from fractal_server.tasks.v2.local import deactivate_local_pixi
29
29
  from fractal_server.tasks.v2.local import reactivate_local
30
+ from fractal_server.tasks.v2.local import reactivate_local_pixi
30
31
  from fractal_server.tasks.v2.ssh import deactivate_ssh
32
+ from fractal_server.tasks.v2.ssh import deactivate_ssh_pixi
31
33
  from fractal_server.tasks.v2.ssh import reactivate_ssh
34
+ from fractal_server.tasks.v2.ssh import reactivate_ssh_pixi
32
35
  from fractal_server.utils import get_timestamp
33
36
 
34
37
  router = APIRouter()
@@ -126,9 +129,12 @@ async def deactivate_task_group(
126
129
  host=user_settings.ssh_host,
127
130
  key_path=user_settings.ssh_private_key_path,
128
131
  )
129
-
132
+ if task_group.origin == TaskGroupV2OriginEnum.PIXI:
133
+ deactivate_function = deactivate_ssh_pixi
134
+ else:
135
+ deactivate_function = deactivate_ssh
130
136
  background_tasks.add_task(
131
- deactivate_ssh,
137
+ deactivate_function,
132
138
  task_group_id=task_group.id,
133
139
  task_group_activity_id=task_group_activity.id,
134
140
  ssh_config=ssh_config,
@@ -252,8 +258,12 @@ async def reactivate_task_group(
252
258
  key_path=user_settings.ssh_private_key_path,
253
259
  )
254
260
 
261
+ if task_group.origin == TaskGroupV2OriginEnum.PIXI:
262
+ reactivate_function = reactivate_ssh_pixi
263
+ else:
264
+ reactivate_function = reactivate_ssh
255
265
  background_tasks.add_task(
256
- reactivate_ssh,
266
+ reactivate_function,
257
267
  task_group_id=task_group.id,
258
268
  task_group_activity_id=task_group_activity.id,
259
269
  ssh_config=ssh_config,
@@ -261,8 +271,12 @@ async def reactivate_task_group(
261
271
  )
262
272
 
263
273
  else:
274
+ if task_group.origin == TaskGroupV2OriginEnum.PIXI:
275
+ reactivate_function = reactivate_local_pixi
276
+ else:
277
+ reactivate_function = reactivate_local
264
278
  background_tasks.add_task(
265
- reactivate_local,
279
+ reactivate_function,
266
280
  task_group_id=task_group.id,
267
281
  task_group_activity_id=task_group_activity.id,
268
282
  )
@@ -204,6 +204,32 @@ class FractalSSH:
204
204
  self.logger.info(f"END reading remote JSON file {filepath}.")
205
205
  return data
206
206
 
207
+ def read_remote_text_file(self, filepath: str) -> dict[str, Any]:
208
+ """
209
+ Read a remote text file into a string.
210
+
211
+ Note from paramiko docs:
212
+ > The Python 'b' flag is ignored, since SSH treats all files as binary.
213
+ """
214
+ self.logger.info(f"START reading remote text file {filepath}.")
215
+ with _acquire_lock_with_timeout(
216
+ lock=self._lock,
217
+ label="read_remote_text_file",
218
+ timeout=self.default_lock_timeout,
219
+ ):
220
+ try:
221
+ with self._sftp_unsafe().open(filepath, "r") as f:
222
+ data = f.read().decode()
223
+ except Exception as e:
224
+ self.log_and_raise(
225
+ e=e,
226
+ message=(
227
+ f"Error in `read_remote_text_file`, for {filepath=}."
228
+ ),
229
+ )
230
+ self.logger.info(f"END reading remote text file {filepath}.")
231
+ return data
232
+
207
233
  def check_connection(self) -> None:
208
234
  """
209
235
  Open the SSH connection and handle exceptions.
@@ -3,3 +3,4 @@ from .collect_pixi import collect_local_pixi # noqa
3
3
  from .deactivate import deactivate_local # noqa
4
4
  from .deactivate_pixi import deactivate_local_pixi # noqa
5
5
  from .reactivate import reactivate_local # noqa
6
+ from .reactivate_pixi import reactivate_local_pixi # noqa
@@ -1,5 +1,4 @@
1
1
  import json
2
- import logging
3
2
  import shutil
4
3
  import time
5
4
  from pathlib import Path
@@ -8,7 +7,6 @@ from tempfile import TemporaryDirectory
8
7
  from ..utils_database import create_db_tasks_and_update_task_group_sync
9
8
  from ._utils import _customize_and_run_template
10
9
  from fractal_server.app.db import get_sync_db
11
- from fractal_server.app.models.v2 import TaskGroupActivityV2
12
10
  from fractal_server.app.models.v2 import TaskGroupV2
13
11
  from fractal_server.app.schemas.v2 import FractalUploadedFile
14
12
  from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
@@ -20,6 +18,9 @@ from fractal_server.tasks.utils import get_log_path
20
18
  from fractal_server.tasks.v2.local._utils import check_task_files_exist
21
19
  from fractal_server.tasks.v2.utils_background import add_commit_refresh
22
20
  from fractal_server.tasks.v2.utils_background import fail_and_cleanup
21
+ from fractal_server.tasks.v2.utils_background import (
22
+ get_activity_and_task_group,
23
+ )
23
24
  from fractal_server.tasks.v2.utils_background import get_current_log
24
25
  from fractal_server.tasks.v2.utils_background import prepare_tasks_metadata
25
26
  from fractal_server.tasks.v2.utils_package_names import compare_package_names
@@ -67,16 +68,12 @@ def collect_local(
67
68
  )
68
69
 
69
70
  with next(get_sync_db()) as db:
70
- # Get main objects from db
71
- activity = db.get(TaskGroupActivityV2, task_group_activity_id)
72
- task_group = db.get(TaskGroupV2, task_group_id)
73
- if activity is None or task_group is None:
74
- # Use `logging` directly
75
- logging.error(
76
- "Cannot find database rows with "
77
- f"{task_group_id=} and {task_group_activity_id=}:\n"
78
- f"{task_group=}\n{activity=}. Exit."
79
- )
71
+ success, task_group, activity = get_activity_and_task_group(
72
+ task_group_activity_id=task_group_activity_id,
73
+ task_group_id=task_group_id,
74
+ db=db,
75
+ )
76
+ if not success:
80
77
  return
81
78
 
82
79
  # Log some info
@@ -1,5 +1,4 @@
1
1
  import json
2
- import logging
3
2
  import shutil
4
3
  import time
5
4
  from pathlib import Path
@@ -9,8 +8,6 @@ from ..utils_database import create_db_tasks_and_update_task_group_sync
9
8
  from ..utils_pixi import parse_collect_stdout
10
9
  from ..utils_pixi import SOURCE_DIR_NAME
11
10
  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
11
  from fractal_server.app.schemas.v2 import FractalUploadedFile
15
12
  from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
16
13
  from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
@@ -24,6 +21,9 @@ from fractal_server.tasks.v2.local._utils import _customize_and_run_template
24
21
  from fractal_server.tasks.v2.local._utils import check_task_files_exist
25
22
  from fractal_server.tasks.v2.utils_background import add_commit_refresh
26
23
  from fractal_server.tasks.v2.utils_background import fail_and_cleanup
24
+ from fractal_server.tasks.v2.utils_background import (
25
+ get_activity_and_task_group,
26
+ )
27
27
  from fractal_server.tasks.v2.utils_background import get_current_log
28
28
  from fractal_server.tasks.v2.utils_background import prepare_tasks_metadata
29
29
  from fractal_server.tasks.v2.utils_templates import SCRIPTS_SUBFOLDER
@@ -48,14 +48,12 @@ def collect_local_pixi(
48
48
  )
49
49
 
50
50
  with next(get_sync_db()) as db:
51
- activity = db.get(TaskGroupActivityV2, task_group_activity_id)
52
- task_group = db.get(TaskGroupV2, task_group_id)
53
- if activity is None or task_group is None:
54
- logging.error(
55
- "Cannot find database rows with "
56
- f"{task_group_id=} and {task_group_activity_id=}:\n"
57
- f"{task_group=}\n{activity=}. Exit."
58
- )
51
+ success, task_group, activity = get_activity_and_task_group(
52
+ task_group_activity_id=task_group_activity_id,
53
+ task_group_id=task_group_id,
54
+ db=db,
55
+ )
56
+ if not success:
59
57
  return
60
58
 
61
59
  logger.info("START")
@@ -75,10 +73,6 @@ def collect_local_pixi(
75
73
  )
76
74
  return
77
75
 
78
- # Set `pixi_bin` and check that it exists
79
- pixi_home = settings.pixi.versions[task_group.pixi_version]
80
- pixi_bin = Path(pixi_home, "bin/pixi").as_posix()
81
-
82
76
  try:
83
77
  Path(task_group.path).mkdir(parents=True)
84
78
  logger.info(f"Created {task_group.path}")
@@ -91,24 +85,20 @@ def collect_local_pixi(
91
85
  task_group.archive_path = archive_path
92
86
  task_group = add_commit_refresh(obj=task_group, db=db)
93
87
 
94
- replacements = {
95
- ("__PIXI_HOME__", pixi_home),
96
- ("__PACKAGE_DIR__", task_group.path),
97
- ("__TAR_GZ_PATH__", archive_path),
98
- (
99
- "__IMPORT_PACKAGE_NAME__",
100
- task_group.pkg_name.replace("-", "_"),
101
- ),
102
- ("__SOURCE_DIR_NAME__", SOURCE_DIR_NAME),
103
- }
104
-
105
- activity.status = TaskGroupActivityStatusV2.ONGOING
106
- activity.log = get_current_log(log_file_path)
107
- activity = add_commit_refresh(obj=activity, db=db)
108
-
109
- stdout = _customize_and_run_template(
110
- template_filename="pixi_1_collect.sh",
111
- replacements=replacements,
88
+ common_args = dict(
89
+ replacements={
90
+ (
91
+ "__PIXI_HOME__",
92
+ settings.pixi.versions[task_group.pixi_version],
93
+ ),
94
+ ("__PACKAGE_DIR__", task_group.path),
95
+ ("__TAR_GZ_PATH__", archive_path),
96
+ (
97
+ "__IMPORT_PACKAGE_NAME__",
98
+ task_group.pkg_name.replace("-", "_"),
99
+ ),
100
+ ("__SOURCE_DIR_NAME__", SOURCE_DIR_NAME),
101
+ },
112
102
  script_dir=Path(
113
103
  task_group.path, SCRIPTS_SUBFOLDER
114
104
  ).as_posix(),
@@ -118,6 +108,32 @@ def collect_local_pixi(
118
108
  ),
119
109
  logger_name=LOGGER_NAME,
120
110
  )
111
+
112
+ activity.status = TaskGroupActivityStatusV2.ONGOING
113
+ activity.log = get_current_log(log_file_path)
114
+ activity = add_commit_refresh(obj=activity, db=db)
115
+
116
+ # Run script 1
117
+ _customize_and_run_template(
118
+ template_filename="pixi_1_extract.sh",
119
+ **common_args,
120
+ )
121
+ activity.log = get_current_log(log_file_path)
122
+ activity = add_commit_refresh(obj=activity, db=db)
123
+
124
+ # Run script 2
125
+ _customize_and_run_template(
126
+ template_filename="pixi_2_install.sh",
127
+ **common_args,
128
+ )
129
+ activity.log = get_current_log(log_file_path)
130
+ activity = add_commit_refresh(obj=activity, db=db)
131
+
132
+ # Run script 3
133
+ stdout = _customize_and_run_template(
134
+ template_filename="pixi_3_post_install.sh",
135
+ **common_args,
136
+ )
121
137
  activity.log = get_current_log(log_file_path)
122
138
  activity = add_commit_refresh(obj=activity, db=db)
123
139
 
@@ -126,6 +142,9 @@ def collect_local_pixi(
126
142
  package_root = parsed_output["package_root"]
127
143
  venv_size = parsed_output["venv_size"]
128
144
  venv_file_number = parsed_output["venv_file_number"]
145
+ project_python_wrapper = parsed_output[
146
+ "project_python_wrapper"
147
+ ]
129
148
 
130
149
  # Read and validate manifest
131
150
  # NOTE: we are only supporting the manifest path being relative
@@ -145,14 +164,7 @@ def collect_local_pixi(
145
164
  package_manifest=pkg_manifest,
146
165
  package_version=task_group.version,
147
166
  package_root=Path(package_root),
148
- pixi_bin=pixi_bin,
149
- pixi_manifest_path=(
150
- Path(
151
- task_group.path,
152
- SOURCE_DIR_NAME,
153
- "pyproject.toml",
154
- ).as_posix()
155
- ),
167
+ project_python_wrapper=Path(project_python_wrapper),
156
168
  )
157
169
  check_task_files_exist(task_list=task_list)
158
170
  logger.info("_prepare_tasks_metadata - end")
@@ -1,4 +1,3 @@
1
- import logging
2
1
  import shutil
3
2
  import time
4
3
  from pathlib import Path
@@ -6,11 +5,10 @@ from tempfile import TemporaryDirectory
6
5
 
7
6
  from ..utils_background import add_commit_refresh
8
7
  from ..utils_background import fail_and_cleanup
8
+ from ..utils_background import get_activity_and_task_group
9
9
  from ..utils_templates import get_collection_replacements
10
10
  from ._utils import _customize_and_run_template
11
11
  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
12
  from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
15
13
  from fractal_server.app.schemas.v2 import TaskGroupV2OriginEnum
16
14
  from fractal_server.app.schemas.v2.task_group import TaskGroupActivityStatusV2
@@ -49,17 +47,12 @@ def deactivate_local(
49
47
  )
50
48
 
51
49
  with next(get_sync_db()) as db:
52
-
53
- # Get main objects from db
54
- activity = db.get(TaskGroupActivityV2, task_group_activity_id)
55
- task_group = db.get(TaskGroupV2, task_group_id)
56
- if activity is None or task_group is None:
57
- # Use `logging` directly
58
- logging.error(
59
- "Cannot find database rows with "
60
- f"{task_group_id=} and {task_group_activity_id=}:\n"
61
- f"{task_group=}\n{activity=}. Exit."
62
- )
50
+ success, task_group, activity = get_activity_and_task_group(
51
+ task_group_activity_id=task_group_activity_id,
52
+ task_group_id=task_group_id,
53
+ db=db,
54
+ )
55
+ if not success:
63
56
  return
64
57
 
65
58
  # Log some info
@@ -1,14 +1,12 @@
1
- import logging
2
1
  import shutil
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_pixi import SOURCE_DIR_NAME
9
9
  from fractal_server.app.db import get_sync_db
10
- from fractal_server.app.models.v2 import TaskGroupActivityV2
11
- from fractal_server.app.models.v2 import TaskGroupV2
12
10
  from fractal_server.app.schemas.v2.task_group import TaskGroupActivityStatusV2
13
11
  from fractal_server.logger import reset_logger_handlers
14
12
  from fractal_server.logger import set_logger
@@ -23,7 +21,7 @@ def deactivate_local_pixi(
23
21
  task_group_id: int,
24
22
  ) -> None:
25
23
  """
26
- Deactivate a task group venv.
24
+ Deactivate a pixi task group venv.
27
25
 
28
26
  This function is run as a background task, therefore exceptions must be
29
27
  handled.
@@ -43,17 +41,12 @@ def deactivate_local_pixi(
43
41
  )
44
42
 
45
43
  with next(get_sync_db()) as db:
46
-
47
- # Get main objects from db
48
- activity = db.get(TaskGroupActivityV2, task_group_activity_id)
49
- task_group = db.get(TaskGroupV2, task_group_id)
50
- if activity is None or task_group is None:
51
- # Use `logging` directly
52
- logging.error(
53
- "Cannot find database rows with "
54
- f"{task_group_id=} and {task_group_activity_id=}:\n"
55
- f"{task_group=}\n{activity=}. Exit."
56
- )
44
+ success, task_group, activity = get_activity_and_task_group(
45
+ task_group_activity_id=task_group_activity_id,
46
+ task_group_id=task_group_id,
47
+ db=db,
48
+ )
49
+ if not success:
57
50
  return
58
51
 
59
52
  # Log some info
@@ -63,7 +56,6 @@ def deactivate_local_pixi(
63
56
  logger.debug(f"task_group.{key}: {value}")
64
57
 
65
58
  source_dir = Path(task_group.path, SOURCE_DIR_NAME)
66
- # Check that the (local) task_group venv_path does exist
67
59
  if not source_dir.exists():
68
60
  error_msg = f"'{source_dir.as_posix()}' does not exist."
69
61
  logger.error(error_msg)
@@ -1,4 +1,3 @@
1
- import logging
2
1
  import shutil
3
2
  import time
4
3
  from pathlib import Path
@@ -6,11 +5,10 @@ from tempfile import TemporaryDirectory
6
5
 
7
6
  from ..utils_background import add_commit_refresh
8
7
  from ..utils_background import fail_and_cleanup
8
+ from ..utils_background import get_activity_and_task_group
9
9
  from ..utils_templates import get_collection_replacements
10
10
  from ._utils import _customize_and_run_template
11
11
  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
12
  from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
15
13
  from fractal_server.app.schemas.v2.task_group import TaskGroupActivityStatusV2
16
14
  from fractal_server.logger import reset_logger_handlers
@@ -50,17 +48,12 @@ def reactivate_local(
50
48
  )
51
49
 
52
50
  with next(get_sync_db()) as db:
53
-
54
- # Get main objects from db
55
- activity = db.get(TaskGroupActivityV2, task_group_activity_id)
56
- task_group = db.get(TaskGroupV2, task_group_id)
57
- if activity is None or task_group is None:
58
- # Use `logging` directly
59
- logging.error(
60
- "Cannot find database rows with "
61
- f"{task_group_id=} and {task_group_activity_id=}:\n"
62
- f"{task_group=}\n{activity=}. Exit."
63
- )
51
+ success, task_group, activity = get_activity_and_task_group(
52
+ task_group_activity_id=task_group_activity_id,
53
+ task_group_id=task_group_id,
54
+ db=db,
55
+ )
56
+ if not success:
64
57
  return
65
58
 
66
59
  # Log some info
@@ -99,7 +92,7 @@ def reactivate_local(
99
92
  replacements.append(
100
93
  ("__PIP_FREEZE_FILE__", f"{tmpdir}/pip_freeze.txt")
101
94
  )
102
- # Prepare common arguments for `_customize_and_run_template``
95
+ # Prepare common arguments for `_customize_and_run_template`
103
96
  common_args = dict(
104
97
  replacements=replacements,
105
98
  script_dir=(
@@ -0,0 +1,166 @@
1
+ import shutil
2
+ import time
3
+ from pathlib import Path
4
+ from tempfile import TemporaryDirectory
5
+
6
+ from ..utils_background import add_commit_refresh
7
+ from ..utils_background import fail_and_cleanup
8
+ from ..utils_background import get_activity_and_task_group
9
+ from ..utils_pixi import SOURCE_DIR_NAME
10
+ from fractal_server.app.db import get_sync_db
11
+ from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
12
+ from fractal_server.app.schemas.v2.task_group import TaskGroupActivityStatusV2
13
+ from fractal_server.config import get_settings
14
+ from fractal_server.logger import reset_logger_handlers
15
+ from fractal_server.logger import set_logger
16
+ from fractal_server.syringe import Inject
17
+ from fractal_server.tasks.utils import get_log_path
18
+ from fractal_server.tasks.v2.local._utils import _customize_and_run_template
19
+ from fractal_server.tasks.v2.utils_background import get_current_log
20
+ from fractal_server.tasks.v2.utils_templates import SCRIPTS_SUBFOLDER
21
+ from fractal_server.utils import get_timestamp
22
+
23
+
24
+ def reactivate_local_pixi(
25
+ *,
26
+ task_group_activity_id: int,
27
+ task_group_id: int,
28
+ ) -> None:
29
+ """
30
+ Reactivate a task group venv.
31
+
32
+ This function is run as a background task, therefore exceptions must be
33
+ handled.
34
+
35
+ Arguments:
36
+ task_group_id:
37
+ task_group_activity_id:
38
+ """
39
+
40
+ LOGGER_NAME = f"{__name__}.ID{task_group_activity_id}"
41
+
42
+ with TemporaryDirectory() as tmpdir:
43
+ log_file_path = get_log_path(Path(tmpdir))
44
+ logger = set_logger(
45
+ logger_name=LOGGER_NAME,
46
+ log_file_path=log_file_path,
47
+ )
48
+ with next(get_sync_db()) as db:
49
+ success, task_group, activity = get_activity_and_task_group(
50
+ task_group_activity_id=task_group_activity_id,
51
+ task_group_id=task_group_id,
52
+ db=db,
53
+ )
54
+ if not success:
55
+ return
56
+
57
+ # Log some info
58
+ logger.debug("START")
59
+
60
+ for key, value in task_group.model_dump().items():
61
+ logger.debug(f"task_group.{key}: {value}")
62
+
63
+ source_dir = Path(task_group.path, SOURCE_DIR_NAME).as_posix()
64
+ if Path(source_dir).exists():
65
+ error_msg = f"{source_dir} already exists."
66
+ logger.error(error_msg)
67
+ fail_and_cleanup(
68
+ task_group=task_group,
69
+ task_group_activity=activity,
70
+ logger_name=LOGGER_NAME,
71
+ log_file_path=log_file_path,
72
+ exception=FileExistsError(error_msg),
73
+ db=db,
74
+ )
75
+ return
76
+
77
+ try:
78
+ activity.status = TaskGroupActivityStatusV2.ONGOING
79
+ activity = add_commit_refresh(obj=activity, db=db)
80
+
81
+ logger.debug(f"start - writing {source_dir}/pixi.lock")
82
+ with Path(source_dir, "pixi.lock").open("w") as f:
83
+ f.write(task_group.env_info)
84
+ logger.debug(f"end - writing {source_dir}/pixi.lock")
85
+
86
+ settings = Inject(get_settings)
87
+ common_args = dict(
88
+ replacements={
89
+ (
90
+ "__PIXI_HOME__",
91
+ settings.pixi.versions[task_group.pixi_version],
92
+ ),
93
+ ("__PACKAGE_DIR__", task_group.path),
94
+ ("__TAR_GZ_PATH__", task_group.archive_path),
95
+ (
96
+ "__IMPORT_PACKAGE_NAME__",
97
+ task_group.pkg_name.replace("-", "_"),
98
+ ),
99
+ ("__SOURCE_DIR_NAME__", SOURCE_DIR_NAME),
100
+ ("__FROZEN_OPTION__", "true"),
101
+ },
102
+ script_dir=Path(
103
+ task_group.path, SCRIPTS_SUBFOLDER
104
+ ).as_posix(),
105
+ prefix=(
106
+ f"{int(time.time())}_"
107
+ f"{TaskGroupActivityActionV2.REACTIVATE}"
108
+ ),
109
+ logger_name=LOGGER_NAME,
110
+ )
111
+
112
+ # Run script 1
113
+ _customize_and_run_template(
114
+ template_filename="pixi_1_extract.sh",
115
+ **common_args,
116
+ )
117
+ activity.log = get_current_log(log_file_path)
118
+ activity = add_commit_refresh(obj=activity, db=db)
119
+
120
+ # Run script 2
121
+ _customize_and_run_template(
122
+ template_filename="pixi_2_install.sh",
123
+ **common_args,
124
+ )
125
+ activity.log = get_current_log(log_file_path)
126
+ activity = add_commit_refresh(obj=activity, db=db)
127
+
128
+ # Run script 3
129
+ _customize_and_run_template(
130
+ template_filename="pixi_3_post_install.sh",
131
+ **common_args,
132
+ )
133
+ activity.log = get_current_log(log_file_path)
134
+ activity = add_commit_refresh(obj=activity, db=db)
135
+
136
+ activity.log = get_current_log(log_file_path)
137
+ activity.status = TaskGroupActivityStatusV2.OK
138
+ activity.timestamp_ended = get_timestamp()
139
+ activity = add_commit_refresh(obj=activity, db=db)
140
+ task_group.active = True
141
+ task_group = add_commit_refresh(obj=task_group, db=db)
142
+ logger.debug("END")
143
+
144
+ reset_logger_handlers(logger)
145
+
146
+ except Exception as reactivate_e:
147
+ # Delete corrupted source_dir
148
+ try:
149
+ logger.info(f"Now delete folder {source_dir}")
150
+ shutil.rmtree(source_dir)
151
+ logger.info(f"Deleted folder {source_dir}")
152
+ except Exception as rm_e:
153
+ logger.error(
154
+ "Removing folder failed.\n"
155
+ f"Original error:\n{str(rm_e)}"
156
+ )
157
+
158
+ fail_and_cleanup(
159
+ task_group=task_group,
160
+ task_group_activity=activity,
161
+ logger_name=LOGGER_NAME,
162
+ log_file_path=log_file_path,
163
+ exception=reactivate_e,
164
+ db=db,
165
+ )
166
+ return
@@ -1,4 +1,6 @@
1
1
  from .collect import collect_ssh # noqa
2
2
  from .collect_pixi import collect_ssh_pixi # noqa
3
3
  from .deactivate import deactivate_ssh # noqa
4
+ from .deactivate_pixi import deactivate_ssh_pixi # noqa
4
5
  from .reactivate import reactivate_ssh # noqa
6
+ from .reactivate_pixi import reactivate_ssh_pixi # noqa
@@ -1,15 +1,13 @@
1
- import logging
2
1
  import time
3
2
  from pathlib import Path
4
3
  from tempfile import TemporaryDirectory
5
4
 
6
5
  from ....ssh._fabric import SingleUseFractalSSH
7
6
  from ..utils_background import fail_and_cleanup
7
+ from ..utils_background import get_activity_and_task_group
8
8
  from ..utils_background import prepare_tasks_metadata
9
9
  from ..utils_database import create_db_tasks_and_update_task_group_sync
10
10
  from fractal_server.app.db import get_sync_db
11
- from fractal_server.app.models.v2 import TaskGroupActivityV2
12
- from fractal_server.app.models.v2 import TaskGroupV2
13
11
  from fractal_server.app.schemas.v2 import FractalUploadedFile
14
12
  from fractal_server.app.schemas.v2 import TaskGroupActivityActionV2
15
13
  from fractal_server.app.schemas.v2 import TaskGroupActivityStatusV2
@@ -76,16 +74,12 @@ def collect_ssh(
76
74
  ) as fractal_ssh:
77
75
 
78
76
  with next(get_sync_db()) as db:
79
- # Get main objects from db
80
- activity = db.get(TaskGroupActivityV2, task_group_activity_id)
81
- task_group = db.get(TaskGroupV2, task_group_id)
82
- if activity is None or task_group is None:
83
- # Use `logging` directly
84
- logging.error(
85
- "Cannot find database rows with "
86
- f"{task_group_id=} and {task_group_activity_id=}:\n"
87
- f"{task_group=}\n{activity=}. Exit."
88
- )
77
+ success, task_group, activity = get_activity_and_task_group(
78
+ task_group_activity_id=task_group_activity_id,
79
+ task_group_id=task_group_id,
80
+ db=db,
81
+ )
82
+ if not success:
89
83
  return
90
84
 
91
85
  # Log some info