fluidattacks_batch_client 0.2.1__tar.gz → 1.0.0__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.
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/PKG-INFO +1 -1
- fluidattacks_batch_client-1.0.0/build/default.nix +27 -0
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/flake.lock +4 -4
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/flake.nix +1 -1
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/fluidattacks_batch_client/__init__.py +5 -4
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/fluidattacks_batch_client/_cli.py +52 -40
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/fluidattacks_batch_client/_logger.py +3 -2
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/fluidattacks_batch_client/_utils.py +22 -27
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/fluidattacks_batch_client/api/_client_1/__init__.py +15 -15
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/fluidattacks_batch_client/api/_client_1/_get_command.py +21 -25
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/fluidattacks_batch_client/api/_client_1/_list_jobs.py +23 -24
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/fluidattacks_batch_client/api/_client_1/_send_job.py +40 -39
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/fluidattacks_batch_client/api/_core.py +13 -14
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/fluidattacks_batch_client/core.py +33 -38
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/fluidattacks_batch_client/decode.py +42 -42
- fluidattacks_batch_client-1.0.0/fluidattacks_batch_client/request.py +39 -0
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/fluidattacks_batch_client/sender/__init__.py +2 -1
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/fluidattacks_batch_client/sender/_core.py +10 -7
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/fluidattacks_batch_client/sender/_send.py +28 -27
- fluidattacks_batch_client-1.0.0/ruff.toml +55 -0
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/tests/arch/arch.py +11 -17
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/tests/arch/test_arch.py +5 -4
- fluidattacks_batch_client-0.2.1/CLAUDE.md +0 -78
- fluidattacks_batch_client-0.2.1/build/default.nix +0 -13
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/.envrc +0 -0
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/build/filter.nix +0 -0
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/fluidattacks_batch_client/api/__init__.py +0 -0
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/fluidattacks_batch_client/py.typed +0 -0
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/mypy.ini +0 -0
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/pyproject.toml +0 -0
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/tests/__init__.py +0 -0
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/tests/arch/__init__.py +0 -0
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/tests/py.typed +0 -0
- {fluidattacks_batch_client-0.2.1 → fluidattacks_batch_client-1.0.0}/uv.lock +0 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
nixpkgs,
|
|
3
|
+
builders,
|
|
4
|
+
scripts,
|
|
5
|
+
src,
|
|
6
|
+
}:
|
|
7
|
+
let
|
|
8
|
+
build_bin =
|
|
9
|
+
bundle:
|
|
10
|
+
nixpkgs.writeShellApplication {
|
|
11
|
+
name = "batch-client";
|
|
12
|
+
runtimeInputs = [
|
|
13
|
+
bundle.env.runtime
|
|
14
|
+
nixpkgs.sops
|
|
15
|
+
];
|
|
16
|
+
text = ''
|
|
17
|
+
batch-client "''${@}"
|
|
18
|
+
'';
|
|
19
|
+
};
|
|
20
|
+
in
|
|
21
|
+
{
|
|
22
|
+
inherit src;
|
|
23
|
+
root_path = "observes/common/batch-client";
|
|
24
|
+
module_name = "fluidattacks_batch_client";
|
|
25
|
+
pypi_token_var = "BATCH_CLIENT_TOKEN";
|
|
26
|
+
override = bundle: bundle // { bin = build_bin bundle; };
|
|
27
|
+
}
|
|
@@ -138,17 +138,17 @@
|
|
|
138
138
|
},
|
|
139
139
|
"locked": {
|
|
140
140
|
"dir": "observes/common/std_flake_2",
|
|
141
|
-
"lastModified":
|
|
142
|
-
"narHash": "sha256-
|
|
141
|
+
"lastModified": 1773763194,
|
|
142
|
+
"narHash": "sha256-6mO8J5MTvf52xScV92c8Pa8ZR02HHy+ZIWMkvcVsdMo=",
|
|
143
143
|
"ref": "refs/heads/trunk",
|
|
144
|
-
"rev": "
|
|
144
|
+
"rev": "46ca331f2ad5676a210651331e002ecd7c89ef04",
|
|
145
145
|
"shallow": true,
|
|
146
146
|
"type": "git",
|
|
147
147
|
"url": "ssh://git@gitlab.com/fluidattacks/universe"
|
|
148
148
|
},
|
|
149
149
|
"original": {
|
|
150
150
|
"dir": "observes/common/std_flake_2",
|
|
151
|
-
"rev": "
|
|
151
|
+
"rev": "46ca331f2ad5676a210651331e002ecd7c89ef04",
|
|
152
152
|
"shallow": true,
|
|
153
153
|
"type": "git",
|
|
154
154
|
"url": "ssh://git@gitlab.com/fluidattacks/universe"
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
inputs = {
|
|
5
5
|
observes_flake_builder = {
|
|
6
|
-
url = "git+ssh://git@gitlab.com/fluidattacks/universe?shallow=1&rev=
|
|
6
|
+
url = "git+ssh://git@gitlab.com/fluidattacks/universe?shallow=1&rev=46ca331f2ad5676a210651331e002ecd7c89ef04&dir=observes/common/std_flake_2";
|
|
7
7
|
};
|
|
8
8
|
};
|
|
9
9
|
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import dataclasses
|
|
2
|
-
|
|
3
|
-
from
|
|
4
|
-
from
|
|
5
|
-
from
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
AllowDuplicates,
|
|
10
|
-
JobPipeline,
|
|
11
|
-
JobRequest,
|
|
12
|
-
JobName,
|
|
2
|
+
import logging
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import (
|
|
6
|
+
IO,
|
|
7
|
+
NoReturn,
|
|
8
|
+
TypeVar,
|
|
13
9
|
)
|
|
10
|
+
|
|
14
11
|
import click
|
|
15
12
|
from fa_purity import (
|
|
16
13
|
Bool,
|
|
@@ -30,49 +27,60 @@ from fa_purity.json import (
|
|
|
30
27
|
UnfoldedFactory,
|
|
31
28
|
Unfolder,
|
|
32
29
|
)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
TypeVar,
|
|
38
|
-
)
|
|
39
|
-
import logging
|
|
30
|
+
|
|
31
|
+
from fluidattacks_batch_client.decode import JobDefDecoder
|
|
32
|
+
from fluidattacks_batch_client.sender import new_sender
|
|
33
|
+
|
|
40
34
|
from . import _utils
|
|
35
|
+
from .api import (
|
|
36
|
+
new_client,
|
|
37
|
+
)
|
|
38
|
+
from .core import (
|
|
39
|
+
AllowDuplicates,
|
|
40
|
+
JobName,
|
|
41
|
+
JobPipeline,
|
|
42
|
+
JobRequest,
|
|
43
|
+
)
|
|
41
44
|
|
|
42
45
|
LOG = logging.getLogger(__name__)
|
|
43
46
|
|
|
44
47
|
_T = TypeVar("_T")
|
|
45
48
|
|
|
46
49
|
|
|
47
|
-
def _read_file(file_path:
|
|
50
|
+
def _read_file(file_path: Path, transform: Callable[[IO[str]], _T]) -> Cmd[_T]:
|
|
48
51
|
def _action() -> _T:
|
|
49
|
-
with open(
|
|
52
|
+
with file_path.open("r", encoding="utf-8") as file:
|
|
50
53
|
return transform(file)
|
|
51
54
|
|
|
52
55
|
return Cmd.wrap_impure(_action)
|
|
53
56
|
|
|
54
57
|
|
|
55
|
-
def _decode_json(file_path:
|
|
58
|
+
def _decode_json(file_path: Path) -> Cmd[ResultE[JsonObj]]:
|
|
56
59
|
return _read_file(file_path, UnfoldedFactory.load)
|
|
57
60
|
|
|
58
61
|
|
|
59
|
-
def _decode_json_list(file_path:
|
|
62
|
+
def _decode_json_list(file_path: Path) -> Cmd[ResultE[FrozenList[JsonObj]]]:
|
|
60
63
|
return _read_file(
|
|
61
64
|
file_path,
|
|
62
65
|
lambda file: JsonValueFactory.load(file).bind(
|
|
63
|
-
lambda v: Unfolder.to_list_of(v, Unfolder.to_json)
|
|
66
|
+
lambda v: Unfolder.to_list_of(v, Unfolder.to_json),
|
|
64
67
|
),
|
|
65
68
|
)
|
|
66
69
|
|
|
67
70
|
|
|
68
71
|
@click.command()
|
|
69
|
-
@click.option(
|
|
72
|
+
@click.option(
|
|
73
|
+
"--job",
|
|
74
|
+
required=True,
|
|
75
|
+
type=click.Path(exists=True, dir_okay=False, path_type=Path),
|
|
76
|
+
help="json encoded file defining a JobRequest",
|
|
77
|
+
)
|
|
70
78
|
@click.option("--allow-duplicates", is_flag=True)
|
|
71
79
|
@click.option("--args-in-name", is_flag=True)
|
|
72
80
|
@click.option("--dry-run", is_flag=True)
|
|
73
81
|
@click.argument("args", nargs=-1) # additional args to append into the job command
|
|
74
82
|
def submit_job(
|
|
75
|
-
job:
|
|
83
|
+
job: Path,
|
|
76
84
|
allow_duplicates: bool,
|
|
77
85
|
args_in_name: bool,
|
|
78
86
|
dry_run: bool,
|
|
@@ -87,16 +95,17 @@ def submit_job(
|
|
|
87
95
|
if dry_run:
|
|
88
96
|
return Cmd.wrap_impure(
|
|
89
97
|
lambda: LOG.info(
|
|
90
|
-
"[dry-run] the following batch job will be sent: %s",
|
|
91
|
-
|
|
98
|
+
"[dry-run] the following batch job will be sent: %s",
|
|
99
|
+
overriden_job,
|
|
100
|
+
),
|
|
92
101
|
)
|
|
93
102
|
return (
|
|
94
103
|
new_client()
|
|
95
104
|
.map(new_sender)
|
|
96
105
|
.bind(
|
|
97
|
-
lambda s: s.send_single_job(
|
|
98
|
-
|
|
99
|
-
)
|
|
106
|
+
lambda s: s.send_single_job(overriden_job, AllowDuplicates(allow_duplicates)).map(
|
|
107
|
+
lambda r: r.alt(Unsafe.raise_exception).to_union(),
|
|
108
|
+
),
|
|
100
109
|
)
|
|
101
110
|
.map(lambda _: None)
|
|
102
111
|
)
|
|
@@ -106,18 +115,21 @@ def submit_job(
|
|
|
106
115
|
lambda decoder: _decode_json(job)
|
|
107
116
|
.map(lambda r: r.bind(decoder.decode_job))
|
|
108
117
|
.map(lambda r: r.alt(Unsafe.raise_exception).to_union())
|
|
109
|
-
.bind(_execute)
|
|
118
|
+
.bind(_execute),
|
|
110
119
|
)
|
|
111
120
|
cmd.compute()
|
|
112
121
|
|
|
113
122
|
|
|
114
123
|
@click.command()
|
|
115
124
|
@click.option(
|
|
116
|
-
"--pipeline",
|
|
125
|
+
"--pipeline",
|
|
126
|
+
required=True,
|
|
127
|
+
type=click.Path(exists=True, dir_okay=False, path_type=Path),
|
|
128
|
+
help="file encoding a list of JobRequest i.e. list of jsons",
|
|
117
129
|
)
|
|
118
130
|
@click.option("--dry-run", is_flag=True)
|
|
119
131
|
def submit_pipeline(
|
|
120
|
-
pipeline:
|
|
132
|
+
pipeline: Path,
|
|
121
133
|
dry_run: bool,
|
|
122
134
|
) -> NoReturn:
|
|
123
135
|
def _execute(job_pipeline: JobPipeline) -> Cmd[UnitType]:
|
|
@@ -125,7 +137,7 @@ def submit_pipeline(
|
|
|
125
137
|
msg = Cmd.wrap_impure(
|
|
126
138
|
lambda: LOG.info(
|
|
127
139
|
"[dry-run] A batch pipeline will be sent",
|
|
128
|
-
)
|
|
140
|
+
),
|
|
129
141
|
)
|
|
130
142
|
|
|
131
143
|
msgs = NewFrozenList(tuple(enumerate(job_pipeline.jobs, start=1))).map(
|
|
@@ -134,8 +146,8 @@ def submit_pipeline(
|
|
|
134
146
|
"[dry-run] [pipeline-job-#%s] this job will be sent: %s",
|
|
135
147
|
j[0],
|
|
136
148
|
j[1],
|
|
137
|
-
)
|
|
138
|
-
)
|
|
149
|
+
),
|
|
150
|
+
),
|
|
139
151
|
)
|
|
140
152
|
jobs_msgs = (
|
|
141
153
|
PureIterFactory.from_list(msgs.items)
|
|
@@ -147,9 +159,9 @@ def submit_pipeline(
|
|
|
147
159
|
new_client()
|
|
148
160
|
.map(new_sender)
|
|
149
161
|
.bind(
|
|
150
|
-
lambda s: s.send_pipeline(
|
|
151
|
-
|
|
152
|
-
)
|
|
162
|
+
lambda s: s.send_pipeline(PureIterFactory.from_list(job_pipeline.jobs.items)).map(
|
|
163
|
+
lambda r: r.alt(Unsafe.raise_exception).to_union(),
|
|
164
|
+
),
|
|
153
165
|
)
|
|
154
166
|
)
|
|
155
167
|
|
|
@@ -158,7 +170,7 @@ def submit_pipeline(
|
|
|
158
170
|
lambda decoder: _decode_json_list(pipeline)
|
|
159
171
|
.map(lambda r: r.map(NewFrozenList).bind(decoder.decode_pipeline))
|
|
160
172
|
.map(lambda r: r.alt(Unsafe.raise_exception).to_union())
|
|
161
|
-
.bind(_execute)
|
|
173
|
+
.bind(_execute),
|
|
162
174
|
)
|
|
163
175
|
cmd.compute()
|
|
164
176
|
|
|
@@ -2,30 +2,32 @@ from __future__ import (
|
|
|
2
2
|
annotations,
|
|
3
3
|
)
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
Callable,
|
|
7
|
-
)
|
|
5
|
+
import os
|
|
8
6
|
from dataclasses import (
|
|
9
7
|
dataclass,
|
|
10
8
|
)
|
|
9
|
+
from typing import (
|
|
10
|
+
TYPE_CHECKING,
|
|
11
|
+
TypeVar,
|
|
12
|
+
)
|
|
13
|
+
|
|
11
14
|
from fa_purity import (
|
|
12
15
|
Cmd,
|
|
16
|
+
Coproduct,
|
|
17
|
+
CoproductFactory,
|
|
13
18
|
FrozenDict,
|
|
14
19
|
FrozenList,
|
|
15
20
|
Maybe,
|
|
16
21
|
PureIter,
|
|
17
22
|
Result,
|
|
18
23
|
ResultE,
|
|
19
|
-
Coproduct,
|
|
20
|
-
CoproductFactory,
|
|
21
24
|
Unsafe,
|
|
22
25
|
)
|
|
23
|
-
from typing import (
|
|
24
|
-
NoReturn,
|
|
25
|
-
TypeVar,
|
|
26
|
-
)
|
|
27
|
-
import os
|
|
28
26
|
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from collections.abc import (
|
|
29
|
+
Callable,
|
|
30
|
+
)
|
|
29
31
|
_T = TypeVar("_T")
|
|
30
32
|
|
|
31
33
|
|
|
@@ -34,9 +36,7 @@ class LibraryBug(Exception):
|
|
|
34
36
|
traceback: Exception
|
|
35
37
|
|
|
36
38
|
def __str__(self) -> str:
|
|
37
|
-
return
|
|
38
|
-
"If raised then there is a bug in the `fluidattacks_batch_client` library"
|
|
39
|
-
)
|
|
39
|
+
return "If raised then there is a bug in the `fluidattacks_batch_client` library"
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
def str_to_int(raw: str) -> ResultE[int]:
|
|
@@ -50,21 +50,21 @@ def int_to_str(item: int) -> str:
|
|
|
50
50
|
return str(item)
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
def handle_value_error(transform: Callable[[], _T
|
|
53
|
+
def handle_value_error(transform: Callable[[], _T]) -> ResultE[_T]:
|
|
54
54
|
try:
|
|
55
55
|
return Result.success(transform())
|
|
56
56
|
except ValueError as err:
|
|
57
57
|
return Result.failure(Exception(err))
|
|
58
58
|
|
|
59
59
|
|
|
60
|
-
def handle_key_error(transform: Callable[[], _T
|
|
60
|
+
def handle_key_error(transform: Callable[[], _T]) -> ResultE[_T]:
|
|
61
61
|
try:
|
|
62
62
|
return Result.success(transform())
|
|
63
63
|
except KeyError as err:
|
|
64
64
|
return Result.failure(Exception(err))
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
def handle_index_error(transform: Callable[[], _T
|
|
67
|
+
def handle_index_error(transform: Callable[[], _T]) -> ResultE[_T]:
|
|
68
68
|
try:
|
|
69
69
|
return Result.success(transform())
|
|
70
70
|
except IndexError as err:
|
|
@@ -72,18 +72,15 @@ def handle_index_error(transform: Callable[[], _T | NoReturn]) -> ResultE[_T]:
|
|
|
72
72
|
|
|
73
73
|
|
|
74
74
|
def get_index(items: FrozenList[_T], index: int) -> Maybe[_T]:
|
|
75
|
-
return Maybe.from_result(
|
|
76
|
-
handle_index_error(lambda: items[index]).alt(lambda _: None)
|
|
77
|
-
)
|
|
75
|
+
return Maybe.from_result(handle_index_error(lambda: items[index]).alt(lambda _: None))
|
|
78
76
|
|
|
79
77
|
|
|
80
78
|
def extract_single(items: PureIter[_T]) -> Coproduct[_T, PureIter[_T]]:
|
|
81
79
|
_factory: CoproductFactory[_T, PureIter[_T]] = CoproductFactory()
|
|
82
80
|
single_element = (
|
|
83
|
-
items.enumerate(1)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
.value_or(True)
|
|
81
|
+
items.enumerate(1).find_first(lambda t: t[0] >= 2).map(lambda _: False).value_or(True) # noqa: PLR2004
|
|
82
|
+
# PLR2004 reason: there is no magic value `2` here
|
|
83
|
+
# the intention is to find the second element of the iterable
|
|
87
84
|
)
|
|
88
85
|
if single_element:
|
|
89
86
|
return _factory.inl(
|
|
@@ -92,11 +89,9 @@ def extract_single(items: PureIter[_T]) -> Coproduct[_T, PureIter[_T]]:
|
|
|
92
89
|
.to_result()
|
|
93
90
|
.alt(lambda _: LibraryBug(Exception("no first element")))
|
|
94
91
|
.alt(Unsafe.raise_exception)
|
|
95
|
-
.to_union()[1]
|
|
92
|
+
.to_union()[1],
|
|
96
93
|
)
|
|
97
94
|
return _factory.inr(items)
|
|
98
95
|
|
|
99
96
|
|
|
100
|
-
get_environment = Cmd[FrozenDict[str, str]].wrap_impure(
|
|
101
|
-
lambda: FrozenDict(dict(os.environ))
|
|
102
|
-
)
|
|
97
|
+
get_environment = Cmd[FrozenDict[str, str]].wrap_impure(lambda: FrozenDict(dict(os.environ)))
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from . import (
|
|
4
|
-
_list_jobs,
|
|
5
|
-
_send_job,
|
|
6
|
-
_get_command,
|
|
7
|
-
)
|
|
8
|
-
from fluidattacks_batch_client.api._core import (
|
|
9
|
-
ApiClient,
|
|
10
|
-
)
|
|
2
|
+
|
|
11
3
|
import boto3
|
|
12
4
|
from fa_purity import (
|
|
13
5
|
Cmd,
|
|
14
6
|
)
|
|
7
|
+
from mypy_boto3_batch.client import (
|
|
8
|
+
BatchClient,
|
|
9
|
+
)
|
|
15
10
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
from fluidattacks_batch_client.api._core import (
|
|
12
|
+
ApiClient,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
from . import (
|
|
16
|
+
_get_command,
|
|
17
|
+
_list_jobs,
|
|
18
|
+
_send_job,
|
|
19
|
+
)
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def _new_batch_client() -> Cmd[BatchClient]:
|
|
23
23
|
def _action() -> BatchClient:
|
|
24
24
|
return boto3.client("batch")
|
|
25
25
|
|
|
26
|
-
return Cmd.wrap_impure(_action)
|
|
26
|
+
return Cmd[BatchClient].wrap_impure(_action)
|
|
27
27
|
|
|
28
28
|
|
|
29
29
|
def new_client() -> Cmd[ApiClient]:
|
|
@@ -32,5 +32,5 @@ def new_client() -> Cmd[ApiClient]:
|
|
|
32
32
|
lambda n, q, s: _list_jobs.list_jobs(client, n, q, s),
|
|
33
33
|
lambda j: _send_job.send_job(client, j),
|
|
34
34
|
lambda j: _get_command.get_command(client, j),
|
|
35
|
-
)
|
|
35
|
+
),
|
|
36
36
|
)
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
2
3
|
from typing import TYPE_CHECKING, TypeVar
|
|
3
4
|
|
|
5
|
+
from fa_purity import (
|
|
6
|
+
Cmd,
|
|
7
|
+
FrozenList,
|
|
8
|
+
Maybe,
|
|
9
|
+
ResultE,
|
|
10
|
+
cast_exception,
|
|
11
|
+
)
|
|
4
12
|
from mypy_boto3_batch.type_defs import (
|
|
5
13
|
ContainerPropertiesOutputTypeDef,
|
|
6
14
|
JobDefinitionTypeDef,
|
|
7
15
|
)
|
|
16
|
+
|
|
8
17
|
from fluidattacks_batch_client.core import (
|
|
9
18
|
Command,
|
|
10
19
|
JobDefinitionName,
|
|
11
20
|
)
|
|
12
|
-
from fa_purity import (
|
|
13
|
-
Cmd,
|
|
14
|
-
FrozenList,
|
|
15
|
-
Maybe,
|
|
16
|
-
Result,
|
|
17
|
-
ResultE,
|
|
18
|
-
cast_exception,
|
|
19
|
-
)
|
|
20
21
|
|
|
21
22
|
if TYPE_CHECKING:
|
|
22
23
|
from mypy_boto3_batch.client import (
|
|
@@ -27,20 +28,16 @@ _T = TypeVar("_T")
|
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
def _decode_command(raw: JobDefinitionTypeDef) -> Maybe[Command]:
|
|
30
|
-
props
|
|
31
|
-
|
|
32
|
-
)
|
|
33
|
-
return props.bind_optional(lambda c: c.get("command")).map(
|
|
34
|
-
lambda c: Command(tuple(c))
|
|
35
|
-
)
|
|
31
|
+
props = Maybe[ContainerPropertiesOutputTypeDef].from_optional(raw.get("containerProperties"))
|
|
32
|
+
return props.bind_optional(lambda c: c.get("command")).map(lambda c: Command(tuple(c)))
|
|
36
33
|
|
|
37
34
|
|
|
38
35
|
def _assert_one(items: FrozenList[_T]) -> ResultE[_T]:
|
|
39
36
|
if len(items) == 0:
|
|
40
|
-
return
|
|
37
|
+
return ResultE[_T].failure(ValueError("list does not have elements"))
|
|
41
38
|
if len(items) > 1:
|
|
42
|
-
return
|
|
43
|
-
return
|
|
39
|
+
return ResultE[_T].failure(ValueError("list has more than one element"))
|
|
40
|
+
return ResultE[_T].success(items[0])
|
|
44
41
|
|
|
45
42
|
|
|
46
43
|
def get_command(
|
|
@@ -49,20 +46,19 @@ def get_command(
|
|
|
49
46
|
) -> Cmd[ResultE[Maybe[Command]]]:
|
|
50
47
|
def _action() -> ResultE[Maybe[Command]]:
|
|
51
48
|
try:
|
|
52
|
-
result = client.describe_job_definitions(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return Result.failure(e)
|
|
49
|
+
result = client.describe_job_definitions(jobDefinitionName=job_def.raw, status="ACTIVE")
|
|
50
|
+
except Exception as e: # noqa: BLE001
|
|
51
|
+
# all errors are intended to be returned
|
|
52
|
+
return ResultE[Maybe[Command]].failure(e)
|
|
57
53
|
return (
|
|
58
54
|
_assert_one(tuple(result["jobDefinitions"]))
|
|
59
55
|
.alt(
|
|
60
56
|
lambda e: ValueError(
|
|
61
|
-
f"Could not determine the current active job definition i.e. {e}"
|
|
62
|
-
)
|
|
57
|
+
f"Could not determine the current active job definition i.e. {e}",
|
|
58
|
+
),
|
|
63
59
|
)
|
|
64
60
|
.alt(cast_exception)
|
|
65
61
|
.map(_decode_command)
|
|
66
62
|
)
|
|
67
63
|
|
|
68
|
-
return Cmd.wrap_impure(_action)
|
|
64
|
+
return Cmd[ResultE[Maybe[Command]]].wrap_impure(_action)
|
|
@@ -1,33 +1,36 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from fluidattacks_batch_client import (
|
|
4
|
-
_utils,
|
|
5
|
-
)
|
|
6
|
-
from fluidattacks_batch_client.core import (
|
|
7
|
-
BatchJob,
|
|
8
|
-
BatchJobObj,
|
|
9
|
-
JobArn,
|
|
10
|
-
JobId,
|
|
11
|
-
JobName,
|
|
12
|
-
JobStatus,
|
|
13
|
-
QueueName,
|
|
14
|
-
)
|
|
2
|
+
|
|
15
3
|
from dataclasses import (
|
|
16
4
|
dataclass,
|
|
17
5
|
)
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
18
8
|
from fa_purity import (
|
|
19
9
|
Cmd,
|
|
20
10
|
FrozenList,
|
|
21
11
|
Maybe,
|
|
12
|
+
PureIterFactory,
|
|
22
13
|
ResultE,
|
|
14
|
+
ResultTransform,
|
|
23
15
|
Stream,
|
|
24
|
-
PureIterFactory,
|
|
25
16
|
StreamFactory,
|
|
26
17
|
StreamTransform,
|
|
27
|
-
ResultTransform,
|
|
28
18
|
Unsafe,
|
|
29
19
|
)
|
|
30
20
|
|
|
21
|
+
from fluidattacks_batch_client import (
|
|
22
|
+
_utils,
|
|
23
|
+
)
|
|
24
|
+
from fluidattacks_batch_client.core import (
|
|
25
|
+
BatchJob,
|
|
26
|
+
BatchJobObj,
|
|
27
|
+
JobArn,
|
|
28
|
+
JobId,
|
|
29
|
+
JobName,
|
|
30
|
+
JobStatus,
|
|
31
|
+
QueueName,
|
|
32
|
+
)
|
|
33
|
+
|
|
31
34
|
if TYPE_CHECKING:
|
|
32
35
|
from mypy_boto3_batch.client import (
|
|
33
36
|
BatchClient,
|
|
@@ -48,7 +51,7 @@ def _decode_job(raw: JobSummaryTypeDef) -> ResultE[BatchJob]:
|
|
|
48
51
|
Maybe.from_optional(raw.get("statusReason")),
|
|
49
52
|
Maybe.from_optional(raw.get("startedAt")),
|
|
50
53
|
Maybe.from_optional(raw.get("stoppedAt")),
|
|
51
|
-
)
|
|
54
|
+
),
|
|
52
55
|
)
|
|
53
56
|
|
|
54
57
|
return _utils.handle_key_error(_inner).bind(lambda x: x)
|
|
@@ -62,7 +65,7 @@ def _decode_job_obj(
|
|
|
62
65
|
_id = JobId(raw["jobId"])
|
|
63
66
|
_name = JobName.new(raw["jobName"])
|
|
64
67
|
return _name.bind(
|
|
65
|
-
lambda name: _decode_job(raw).map(lambda j: BatchJobObj(_id, _arn, name, j))
|
|
68
|
+
lambda name: _decode_job(raw).map(lambda j: BatchJobObj(_id, _arn, name, j)),
|
|
66
69
|
)
|
|
67
70
|
|
|
68
71
|
return _utils.handle_key_error(_inner).bind(lambda x: x)
|
|
@@ -76,9 +79,7 @@ class JobsPage:
|
|
|
76
79
|
|
|
77
80
|
def _decode_respose(response: ListJobsResponseTypeDef) -> ResultE[JobsPage]:
|
|
78
81
|
def _inner() -> ResultE[JobsPage]:
|
|
79
|
-
items = PureIterFactory.from_list(response["jobSummaryList"]).map(
|
|
80
|
-
_decode_job_obj
|
|
81
|
-
)
|
|
82
|
+
items = PureIterFactory.from_list(response["jobSummaryList"]).map(_decode_job_obj)
|
|
82
83
|
_next = Maybe.from_optional(response.get("nextToken"))
|
|
83
84
|
return ResultTransform.all_ok(items.to_list()).map(lambda i: JobsPage(i, _next))
|
|
84
85
|
|
|
@@ -92,11 +93,9 @@ def _list_jobs_page(
|
|
|
92
93
|
_next: Maybe[str],
|
|
93
94
|
) -> Cmd[JobsPage]:
|
|
94
95
|
def _action() -> JobsPage:
|
|
95
|
-
_filter: FrozenList[KeyValuesPairTypeDef] = (
|
|
96
|
-
{"name": "JOB_NAME", "values": [name.raw]},
|
|
97
|
-
)
|
|
96
|
+
_filter: FrozenList[KeyValuesPairTypeDef] = ({"name": "JOB_NAME", "values": [name.raw]},)
|
|
98
97
|
result = _next.map(
|
|
99
|
-
lambda n: client.list_jobs(jobQueue=queue.raw, filters=_filter, nextToken=n)
|
|
98
|
+
lambda n: client.list_jobs(jobQueue=queue.raw, filters=_filter, nextToken=n),
|
|
100
99
|
).or_else_call(lambda: client.list_jobs(jobQueue=queue.raw, filters=_filter))
|
|
101
100
|
return _decode_respose(result).alt(Unsafe.raise_exception).to_union()
|
|
102
101
|
|