quapp-hpc 0.0.1.dev4__tar.gz → 0.0.1.dev6__tar.gz
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.
- {quapp_hpc-0.0.1.dev4/quapp_hpc.egg-info → quapp_hpc-0.0.1.dev6}/PKG-INFO +2 -2
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/pyproject.toml +3 -3
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc/factory/hpc_provider_factory.py +1 -1
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc/model/provider/slurm_provider.py +1 -1
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6/quapp_hpc.egg-info}/PKG-INFO +2 -2
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc.egg-info/SOURCES.txt +3 -1
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc.egg-info/requires.txt +1 -1
- quapp_hpc-0.0.1.dev6/quapp_hpc.egg-info/top_level.txt +2 -0
- quapp_hpc-0.0.1.dev6/quapp_slurm_hpc/__init__.py +0 -0
- quapp_hpc-0.0.1.dev6/quapp_slurm_hpc/runner.py +112 -0
- quapp_hpc-0.0.1.dev4/quapp_hpc.egg-info/top_level.txt +0 -1
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/LICENSE +0 -0
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/README.md +0 -0
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc/__init__.py +0 -0
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc/component/__init__.py +0 -0
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc/component/backend/__init__.py +0 -0
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc/component/backend/hpc_invocation.py +0 -0
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc/component/backend/slurm_job_fetching.py +0 -0
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc/factory/__init__.py +0 -0
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc/factory/hpc_device_factory.py +0 -0
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc/factory/hpc_handler_factory.py +0 -0
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc/handler/__init__.py +0 -0
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc/handler/invocation_handler.py +0 -0
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc/handler/job_fetching_handler.py +0 -0
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc/model/__init__.py +0 -0
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc/model/device/__init__.py +0 -0
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc/model/device/slurm_device.py +0 -0
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc/model/provider/__init__.py +0 -0
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc.egg-info/dependency_links.txt +0 -0
- {quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quapp-hpc
|
|
3
|
-
Version: 0.0.1.
|
|
3
|
+
Version: 0.0.1.dev6
|
|
4
4
|
Summary: Quapp HPC library — Slurm integration for Quapp Platform
|
|
5
5
|
Author-email: "CITYNOW Co. Ltd." <corp@citynow.vn>
|
|
6
6
|
License: The MIT License (MIT)
|
|
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
19
19
|
Requires-Python: <3.13,>=3.10
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
-
Requires-Dist: quapp-common==0.0.12.
|
|
22
|
+
Requires-Dist: quapp-common==0.0.12.dev12
|
|
23
23
|
Requires-Dist: requests>=2.31.0
|
|
24
24
|
Requires-Dist: boto3>=1.28.0
|
|
25
25
|
Provides-Extra: dev
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "quapp-hpc"
|
|
7
|
-
version = "0.0.1.
|
|
7
|
+
version = "0.0.1.dev6"
|
|
8
8
|
description = "Quapp HPC library — Slurm integration for Quapp Platform"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "CITYNOW Co. Ltd.", email = "corp@citynow.vn" }]
|
|
@@ -16,7 +16,7 @@ classifiers = [
|
|
|
16
16
|
]
|
|
17
17
|
keywords = ["quapp", "quapp-hpc", "slurm", "hpc"]
|
|
18
18
|
dependencies = [
|
|
19
|
-
"quapp-common==0.0.12.
|
|
19
|
+
"quapp-common==0.0.12.dev12",
|
|
20
20
|
"requests>=2.31.0",
|
|
21
21
|
"boto3>=1.28.0",
|
|
22
22
|
]
|
|
@@ -26,7 +26,7 @@ requires-python = ">=3.10,<3.13"
|
|
|
26
26
|
dev = ["black", "bumpver", "isort", "pip-tools", "pytest"]
|
|
27
27
|
|
|
28
28
|
[tool.setuptools.packages.find]
|
|
29
|
-
include = ["quapp_hpc*"]
|
|
29
|
+
include = ["quapp_hpc*", "quapp_slurm_hpc*"]
|
|
30
30
|
exclude = ["*.md", "*.yml", "*.yaml", "*.toml", "tests*", ".gitignore"]
|
|
31
31
|
|
|
32
32
|
[project.urls]
|
|
@@ -15,7 +15,7 @@ class HpcProviderFactory:
|
|
|
15
15
|
def create_provider(provider_type: ProviderTag, authentication: dict):
|
|
16
16
|
logger.debug(f"Creating HPC provider: {provider_type}")
|
|
17
17
|
|
|
18
|
-
if provider_type == ProviderTag.
|
|
18
|
+
if provider_type == ProviderTag.QUAPP_HPC:
|
|
19
19
|
jwt = authentication.get("slurm_jwt") or SLURM_JWT
|
|
20
20
|
if not jwt:
|
|
21
21
|
raise ValueError("SLURM_JWT not set — cannot authenticate with Slurm API")
|
|
@@ -15,7 +15,7 @@ SLURM_ACCOUNT = os.getenv("SLURM_ACCOUNT", "quapp")
|
|
|
15
15
|
class SlurmProvider(Provider):
|
|
16
16
|
|
|
17
17
|
def __init__(self, jwt_token: str):
|
|
18
|
-
super().__init__(ProviderTag.
|
|
18
|
+
super().__init__(ProviderTag.QUAPP_HPC)
|
|
19
19
|
self.jwt_token = jwt_token
|
|
20
20
|
self.base_url = f"{SLURM_API_URL}/slurm/{SLURM_API_VER}"
|
|
21
21
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quapp-hpc
|
|
3
|
-
Version: 0.0.1.
|
|
3
|
+
Version: 0.0.1.dev6
|
|
4
4
|
Summary: Quapp HPC library — Slurm integration for Quapp Platform
|
|
5
5
|
Author-email: "CITYNOW Co. Ltd." <corp@citynow.vn>
|
|
6
6
|
License: The MIT License (MIT)
|
|
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
19
19
|
Requires-Python: <3.13,>=3.10
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
License-File: LICENSE
|
|
22
|
-
Requires-Dist: quapp-common==0.0.12.
|
|
22
|
+
Requires-Dist: quapp-common==0.0.12.dev12
|
|
23
23
|
Requires-Dist: requests>=2.31.0
|
|
24
24
|
Requires-Dist: boto3>=1.28.0
|
|
25
25
|
Provides-Extra: dev
|
|
@@ -22,4 +22,6 @@ quapp_hpc/model/__init__.py
|
|
|
22
22
|
quapp_hpc/model/device/__init__.py
|
|
23
23
|
quapp_hpc/model/device/slurm_device.py
|
|
24
24
|
quapp_hpc/model/provider/__init__.py
|
|
25
|
-
quapp_hpc/model/provider/slurm_provider.py
|
|
25
|
+
quapp_hpc/model/provider/slurm_provider.py
|
|
26
|
+
quapp_slurm_hpc/__init__.py
|
|
27
|
+
quapp_slurm_hpc/runner.py
|
|
File without changes
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""CLI runner — invoked by the sbatch script inside the Apptainer container.
|
|
2
|
+
|
|
3
|
+
The Java SlurmPlatformManager generates a sbatch script that runs:
|
|
4
|
+
|
|
5
|
+
apptainer exec docker://<image> python -m quapp_slurm_hpc.runner \
|
|
6
|
+
--input-file /data/jobs/<jobId>/input.json \
|
|
7
|
+
--output-file /data/jobs/<jobId>/output.json \
|
|
8
|
+
--handler-dir /home/app/function
|
|
9
|
+
|
|
10
|
+
This module:
|
|
11
|
+
1. Reads the invocation input from --input-file (JSON written by the sbatch script).
|
|
12
|
+
2. Loads handler.py from --handler-dir.
|
|
13
|
+
3. Calls handler.processing(invocation_input) → bash script string.
|
|
14
|
+
4. Executes the bash script, captures stdout / stderr / exit_code.
|
|
15
|
+
5. Calls handler.post_processing(raw_result) if it exists.
|
|
16
|
+
6. Writes the final result dict to --output-file.
|
|
17
|
+
"""
|
|
18
|
+
import argparse
|
|
19
|
+
import importlib.util
|
|
20
|
+
import json
|
|
21
|
+
import subprocess
|
|
22
|
+
import sys
|
|
23
|
+
import tempfile
|
|
24
|
+
import traceback
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
from types import ModuleType
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _load_handler(handler_dir: str) -> ModuleType:
|
|
30
|
+
handler_path = Path(handler_dir) / "handler.py"
|
|
31
|
+
if not handler_path.exists():
|
|
32
|
+
raise FileNotFoundError(
|
|
33
|
+
f"handler.py not found in {handler_dir!r}. "
|
|
34
|
+
"Ensure the function image was built with the handler in /home/app/function."
|
|
35
|
+
)
|
|
36
|
+
if str(handler_dir) not in sys.path:
|
|
37
|
+
sys.path.insert(0, str(handler_dir))
|
|
38
|
+
spec = importlib.util.spec_from_file_location("handler", handler_path)
|
|
39
|
+
module = importlib.util.module_from_spec(spec)
|
|
40
|
+
spec.loader.exec_module(module)
|
|
41
|
+
return module
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _run_script(script: str) -> dict:
|
|
45
|
+
"""Write script to a temp file and execute it with bash, capturing output."""
|
|
46
|
+
with tempfile.NamedTemporaryFile(
|
|
47
|
+
mode="w", suffix=".sh", delete=False, encoding="utf-8"
|
|
48
|
+
) as f:
|
|
49
|
+
f.write(script)
|
|
50
|
+
tmp_path = f.name
|
|
51
|
+
try:
|
|
52
|
+
result = subprocess.run(
|
|
53
|
+
["bash", tmp_path],
|
|
54
|
+
capture_output=True,
|
|
55
|
+
text=True,
|
|
56
|
+
)
|
|
57
|
+
return {
|
|
58
|
+
"exit_code": result.returncode,
|
|
59
|
+
"stdout": result.stdout,
|
|
60
|
+
"stderr": result.stderr,
|
|
61
|
+
}
|
|
62
|
+
finally:
|
|
63
|
+
Path(tmp_path).unlink(missing_ok=True)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def main() -> None:
|
|
67
|
+
parser = argparse.ArgumentParser(
|
|
68
|
+
description="Quapp HPC runner — executes handler.py inside an Apptainer container"
|
|
69
|
+
)
|
|
70
|
+
parser.add_argument("--input-file", required=True, help="Path to input JSON file")
|
|
71
|
+
parser.add_argument("--output-file", required=True, help="Path to write output JSON")
|
|
72
|
+
parser.add_argument("--handler-dir", required=True, help="Directory containing handler.py")
|
|
73
|
+
args = parser.parse_args()
|
|
74
|
+
|
|
75
|
+
output: dict = {}
|
|
76
|
+
try:
|
|
77
|
+
with open(args.input_file, encoding="utf-8") as f:
|
|
78
|
+
invocation_input = json.load(f)
|
|
79
|
+
|
|
80
|
+
handler = _load_handler(args.handler_dir)
|
|
81
|
+
|
|
82
|
+
if not hasattr(handler, "processing"):
|
|
83
|
+
raise AttributeError(
|
|
84
|
+
"handler.py must define a processing(invocation_input: dict) -> str function"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
script = handler.processing(invocation_input)
|
|
88
|
+
if not isinstance(script, str):
|
|
89
|
+
raise TypeError(
|
|
90
|
+
f"handler.processing() must return a str (bash script), got {type(script).__name__}"
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
raw_result = _run_script(script)
|
|
94
|
+
|
|
95
|
+
if hasattr(handler, "post_processing"):
|
|
96
|
+
output = handler.post_processing(raw_result)
|
|
97
|
+
else:
|
|
98
|
+
output = raw_result
|
|
99
|
+
|
|
100
|
+
except Exception:
|
|
101
|
+
output = {
|
|
102
|
+
"exit_code": 1,
|
|
103
|
+
"stdout": "",
|
|
104
|
+
"stderr": traceback.format_exc(),
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
with open(args.output_file, "w", encoding="utf-8") as f:
|
|
108
|
+
json.dump(output, f)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
if __name__ == "__main__":
|
|
112
|
+
main()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
quapp_hpc
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_hpc-0.0.1.dev4 → quapp_hpc-0.0.1.dev6}/quapp_hpc/component/backend/slurm_job_fetching.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|