karton-core 5.7.0__py3-none-any.whl → 5.8.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.
karton/core/karton.py CHANGED
@@ -137,7 +137,7 @@ class Consumer(KartonServiceBase):
137
137
  )
138
138
  if self.task_timeout is None:
139
139
  self.task_timeout = self.config.getint("karton", "task_timeout")
140
- self.current_task: Optional[Task] = None
140
+
141
141
  self._pre_hooks: List[Tuple[Optional[str], Callable[[Task], None]]] = []
142
142
  self._post_hooks: List[
143
143
  Tuple[
@@ -170,19 +170,18 @@ class Consumer(KartonServiceBase):
170
170
  """
171
171
 
172
172
  self.current_task = task
173
- self.log_handler.set_task(self.current_task)
174
173
 
175
- if not self.current_task.matches_filters(self.filters):
174
+ if not task.matches_filters(self.filters):
176
175
  self.log.info("Task rejected because binds are no longer valid.")
177
- self.backend.set_task_status(self.current_task, TaskState.FINISHED)
176
+ self.backend.set_task_status(task, TaskState.FINISHED)
178
177
  # Task rejected: end of processing
179
178
  return
180
179
 
181
180
  exception_str = None
182
181
 
183
182
  try:
184
- self.log.info("Received new task - %s", self.current_task.uid)
185
- self.backend.set_task_status(self.current_task, TaskState.STARTED)
183
+ self.log.info("Received new task - %s", task.uid)
184
+ self.backend.set_task_status(task, TaskState.STARTED)
186
185
 
187
186
  self._run_pre_hooks()
188
187
 
@@ -190,22 +189,22 @@ class Consumer(KartonServiceBase):
190
189
  try:
191
190
  if self.task_timeout:
192
191
  with timeout(self.task_timeout):
193
- self.process(self.current_task)
192
+ self.process(task)
194
193
  else:
195
- self.process(self.current_task)
194
+ self.process(task)
196
195
  except (Exception, TaskTimeoutError) as exc:
197
196
  saved_exception = exc
198
197
  raise
199
198
  finally:
200
199
  self._run_post_hooks(saved_exception)
201
200
 
202
- self.log.info("Task done - %s", self.current_task.uid)
201
+ self.log.info("Task done - %s", task.uid)
203
202
  except (Exception, TaskTimeoutError):
204
203
  exc_info = sys.exc_info()
205
204
  exception_str = traceback.format_exception(*exc_info)
206
205
 
207
206
  self.backend.increment_metrics(KartonMetrics.TASK_CRASHED, self.identity)
208
- self.log.exception("Failed to process task - %s", self.current_task.uid)
207
+ self.log.exception("Failed to process task - %s", task.uid)
209
208
  finally:
210
209
  self.backend.increment_metrics(KartonMetrics.TASK_CONSUMED, self.identity)
211
210
 
@@ -215,9 +214,10 @@ class Consumer(KartonServiceBase):
215
214
  # if an exception was caught while processing
216
215
  if exception_str is not None:
217
216
  task_state = TaskState.CRASHED
218
- self.current_task.error = exception_str
217
+ task.error = exception_str
219
218
 
220
- self.backend.set_task_status(self.current_task, task_state)
219
+ self.backend.set_task_status(task, task_state)
220
+ self.current_task = None
221
221
 
222
222
  @property
223
223
  def _bind(self) -> KartonBind:
@@ -228,6 +228,7 @@ class Consumer(KartonServiceBase):
228
228
  filters=self.filters,
229
229
  persistent=self.persistent,
230
230
  service_version=self.__class__.version,
231
+ is_async=False,
231
232
  )
232
233
 
233
234
  @classmethod
karton/core/logger.py CHANGED
@@ -2,30 +2,32 @@ import logging
2
2
  import platform
3
3
  import traceback
4
4
  import warnings
5
- from typing import Optional
5
+ from typing import Any, Callable, Dict
6
6
 
7
7
  from .backend import KartonBackend
8
- from .task import Task
8
+ from .task import get_current_task
9
9
 
10
10
  HOSTNAME = platform.node()
11
11
 
12
12
 
13
- class KartonLogHandler(logging.Handler):
13
+ class TaskContextFilter(logging.Filter):
14
14
  """
15
- logging.Handler that passes logs to the Karton backend.
15
+ This is a filter which injects information about current task ID to the log.
16
16
  """
17
17
 
18
- def __init__(self, backend: KartonBackend, channel: str) -> None:
19
- logging.Handler.__init__(self)
20
- self.backend = backend
21
- self.task: Optional[Task] = None
22
- self.is_consumer_active: bool = True
23
- self.channel: str = channel
18
+ def filter(self, record: logging.LogRecord) -> bool:
19
+ current_task = get_current_task()
20
+ if current_task is not None:
21
+ record.task_id = current_task.task_uid
22
+ else:
23
+ record.task_id = "(no task)"
24
+ return True
24
25
 
25
- def set_task(self, task: Task) -> None:
26
- self.task = task
27
26
 
28
- def emit(self, record: logging.LogRecord) -> None:
27
+ class LogLineFormatterMixin:
28
+ format: Callable[[logging.LogRecord], str]
29
+
30
+ def prepare_log_line(self, record: logging.LogRecord) -> Dict[str, Any]:
29
31
  ignore_fields = [
30
32
  "args",
31
33
  "asctime",
@@ -54,11 +56,27 @@ class KartonLogHandler(logging.Handler):
54
56
  log_line["type"] = "log"
55
57
  log_line["message"] = self.format(record)
56
58
 
57
- if self.task is not None:
58
- log_line["task"] = self.task.serialize()
59
+ current_task = get_current_task()
60
+ if current_task is not None:
61
+ log_line["task"] = current_task.serialize()
59
62
 
60
63
  log_line["hostname"] = HOSTNAME
64
+ return log_line
65
+
66
+
67
+ class KartonLogHandler(logging.Handler, LogLineFormatterMixin):
68
+ """
69
+ logging.Handler that passes logs to the Karton backend.
70
+ """
71
+
72
+ def __init__(self, backend: KartonBackend, channel: str) -> None:
73
+ logging.Handler.__init__(self)
74
+ self.backend = backend
75
+ self.is_consumer_active: bool = True
76
+ self.channel: str = channel
61
77
 
78
+ def emit(self, record: logging.LogRecord) -> None:
79
+ log_line = self.prepare_log_line(record)
62
80
  log_consumed = self.backend.produce_log(
63
81
  log_line, logger_name=self.channel, level=record.levelname
64
82
  )
karton/core/resource.py CHANGED
@@ -150,34 +150,7 @@ class ResourceBase(object):
150
150
  }
151
151
 
152
152
 
153
- class LocalResource(ResourceBase):
154
- """
155
- Represents local resource with arbitrary binary data e.g. file contents.
156
-
157
- Local resources will be uploaded to object hub (S3) during
158
- task dispatching.
159
-
160
- .. code-block:: python
161
-
162
- # Creating resource from bytes
163
- sample = Resource("original_name.exe", content=b"X5O!P%@AP[4\\
164
- PZX54(P^)7CC)7}$EICAR-STANDARD-ANT...")
165
-
166
- # Creating resource from path
167
- sample = Resource("original_name.exe", path="sample/original_name.exe")
168
-
169
- :param name: Name of the resource (e.g. name of file)
170
- :param content: Resource content
171
- :param path: Path of file with resource content
172
- :param bucket: Alternative S3 bucket for resource
173
- :param metadata: Resource metadata
174
- :param uid: Alternative S3 resource id
175
- :param sha256: Resource sha256 hash
176
- :param fd: Seekable file descriptor
177
- :param _flags: Resource flags
178
- :param _close_fd: Close file descriptor after upload (default: False)
179
- """
180
-
153
+ class LocalResourceBase(ResourceBase):
181
154
  def __init__(
182
155
  self,
183
156
  name: str,
@@ -194,7 +167,7 @@ class LocalResource(ResourceBase):
194
167
  if len(list(filter(None, [path, content, fd]))) != 1:
195
168
  raise ValueError("You must exclusively provide a path, content or fd")
196
169
 
197
- super(LocalResource, self).__init__(
170
+ super().__init__(
198
171
  name,
199
172
  content=content,
200
173
  path=path,
@@ -247,7 +220,7 @@ class LocalResource(ResourceBase):
247
220
  bucket: Optional[str] = None,
248
221
  metadata: Optional[Dict[str, Any]] = None,
249
222
  uid: Optional[str] = None,
250
- ) -> "LocalResource":
223
+ ) -> "LocalResourceBase":
251
224
  """
252
225
  Resource extension, allowing to pass whole directory as a zipped resource.
253
226
 
@@ -305,6 +278,35 @@ class LocalResource(ResourceBase):
305
278
  _close_fd=True,
306
279
  )
307
280
 
281
+
282
+ class LocalResource(LocalResourceBase):
283
+ """
284
+ Represents local resource with arbitrary binary data e.g. file contents.
285
+
286
+ Local resources will be uploaded to object hub (S3) during
287
+ task dispatching.
288
+
289
+ .. code-block:: python
290
+
291
+ # Creating resource from bytes
292
+ sample = Resource("original_name.exe", content=b"X5O!P%@AP[4\\
293
+ PZX54(P^)7CC)7}$EICAR-STANDARD-ANT...")
294
+
295
+ # Creating resource from path
296
+ sample = Resource("original_name.exe", path="sample/original_name.exe")
297
+
298
+ :param name: Name of the resource (e.g. name of file)
299
+ :param content: Resource content
300
+ :param path: Path of file with resource content
301
+ :param bucket: Alternative S3 bucket for resource
302
+ :param metadata: Resource metadata
303
+ :param uid: Alternative S3 resource id
304
+ :param sha256: Resource sha256 hash
305
+ :param fd: Seekable file descriptor
306
+ :param _flags: Resource flags
307
+ :param _close_fd: Close file descriptor after upload (default: False)
308
+ """
309
+
308
310
  def _upload(self, backend: "KartonBackend") -> None:
309
311
  """Internal function for uploading resources
310
312
 
karton/core/task.py CHANGED
@@ -3,6 +3,7 @@ import json
3
3
  import time
4
4
  import uuid
5
5
  import warnings
6
+ from contextvars import ContextVar
6
7
  from typing import (
7
8
  TYPE_CHECKING,
8
9
  Any,
@@ -24,6 +25,16 @@ if TYPE_CHECKING:
24
25
 
25
26
  import orjson
26
27
 
28
+ current_task: ContextVar[Optional["Task"]] = ContextVar("current_task")
29
+
30
+
31
+ def get_current_task() -> Optional["Task"]:
32
+ return current_task.get(None)
33
+
34
+
35
+ def set_current_task(task: Optional["Task"]):
36
+ current_task.set(task)
37
+
27
38
 
28
39
  class TaskState(enum.Enum):
29
40
  DECLARED = "Declared" # Task declared in TASKS_QUEUE
@@ -375,12 +386,15 @@ class Task(object):
375
386
  data: Union[str, bytes],
376
387
  backend: Optional["KartonBackend"] = None,
377
388
  parse_resources: bool = True,
389
+ resource_unserializer: Optional[Callable[[Dict], Any]] = None,
378
390
  ) -> "Task":
379
391
  """
380
392
  Unserialize Task instance from JSON string
381
393
 
382
394
  :param data: JSON-serialized task
383
- :param backend: Backend instance to be bound to RemoteResource objects
395
+ :param backend: |
396
+ Backend instance to be bound to RemoteResource objects.
397
+ Deprecated: pass resource_unserializer instead.
384
398
  :param parse_resources: |
385
399
  If set to False (default is True), method doesn't
386
400
  deserialize '__karton_resource__' entries, which speeds up deserialization
@@ -388,6 +402,9 @@ class Task(object):
388
402
  filtering based on status.
389
403
  When resource deserialization is turned off, Task.unserialize will try
390
404
  to use faster 3rd-party JSON parser (orjson).
405
+ :param resource_unserializer: |
406
+ Resource factory used for deserialization of __karton_resource__
407
+ dictionary values.
391
408
  :return: Unserialized Task object
392
409
 
393
410
  :meta private:
@@ -399,7 +416,12 @@ class Task(object):
399
416
  RemoteResource object instances
400
417
  """
401
418
  if isinstance(value, dict) and "__karton_resource__" in value:
402
- return RemoteResource.from_dict(value["__karton_resource__"], backend)
419
+ if resource_unserializer is None:
420
+ return RemoteResource.from_dict(
421
+ value["__karton_resource__"], backend
422
+ )
423
+ else:
424
+ return resource_unserializer(value["__karton_resource__"])
403
425
  return value
404
426
 
405
427
  if not isinstance(data, str):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: karton-core
3
- Version: 5.7.0
3
+ Version: 5.8.0
4
4
  Summary: Distributed malware analysis orchestration framework
5
5
  Home-page: https://github.com/CERT-Polska/karton
6
6
  Classifier: Programming Language :: Python :: 3
@@ -9,7 +9,8 @@ Classifier: License :: OSI Approved :: BSD License
9
9
  Requires-Python: >=3.8
10
10
  Description-Content-Type: text/markdown
11
11
  License-File: LICENSE
12
- Requires-Dist: boto3 <1.36.0
12
+ Requires-Dist: aioboto3 ==14.3.0
13
+ Requires-Dist: boto3 <1.37.4,>=1.37.2
13
14
  Requires-Dist: orjson
14
15
  Requires-Dist: redis
15
16
 
@@ -0,0 +1,33 @@
1
+ karton_core-5.8.0-nspkg.pth,sha256=vHa-jm6pBTeInFrmnsHMg9AOeD88czzQy-6QCFbpRcM,539
2
+ karton/core/__init__.py,sha256=QuT0BWZyp799eY90tK3H1OD2hwuusqMJq8vQwpB3kG4,337
3
+ karton/core/__version__.py,sha256=duDTiv1rL8Ee9_jtzzhpq-j4xkkpVPkUh2Daa_Ou-xA,22
4
+ karton/core/backend.py,sha256=3tmjAM35jaoZr_5QqarcVH81LvKMEB63vyvMKCYXPCM,39934
5
+ karton/core/base.py,sha256=6PEQiEWHjMtJKRj0dfgEEpNhgSRoKOBYF1WBlrmyBp0,9064
6
+ karton/core/config.py,sha256=UTd0hLqYNUttfI7FYUDOJPaz-C3uj1Kw7xxataTG_OM,8234
7
+ karton/core/exceptions.py,sha256=8i9WVzi4PinNlX10Cb-lQQC35Hl-JB5R_UKXa9AUKoQ,153
8
+ karton/core/inspect.py,sha256=aIJQEOEkD5q2xLlV8nhxY5qL5zqcnprP-2DdP6ecKlE,6150
9
+ karton/core/karton.py,sha256=4CISOmUTfaEaCJUtbYxJSBMzydT27o3a-R14VBNpmr0,15269
10
+ karton/core/logger.py,sha256=UhYCoVARXaatvoJ2lO2mfBHeODOS7z8O-vqdeQhNmV4,2654
11
+ karton/core/main.py,sha256=ir1-dhn3vbwfh2YHiM6ZYfRBbjwLvJSz0d8tuK1mb_4,8310
12
+ karton/core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ karton/core/query.py,sha256=sf24DweVlXfJuBbBD_ns2LXhOV-IBwuPG3jBfTJu77s,12063
14
+ karton/core/resource.py,sha256=GkU3JGSP1kOsAoblon4BWbQp31eZiDsCjaaDKanEokk,20872
15
+ karton/core/task.py,sha256=QvAsSUIsyYiRmkgxgugrYkHWH2gczrFDfw2V4izdt1E,19566
16
+ karton/core/test.py,sha256=cj6W4gNt0BpRjsYiiBt0hPE8dmRfUeIc8sSVkxB50cU,9123
17
+ karton/core/utils.py,sha256=sEVqGdVPyYswWuVn8wYXBQmln8Az826N_2HgC__pmW8,4090
18
+ karton/core/asyncio/__init__.py,sha256=ZgndeKzS3Yg2o8hebwFYJWlCRdW3ImdCOShK4EVmZ14,457
19
+ karton/core/asyncio/backend.py,sha256=GF0z9YxWvUKYkvnBatCDthX0M9Kwt9cRfVSWQq5bc9E,12751
20
+ karton/core/asyncio/base.py,sha256=YDNGyWzgVvt2TnfKvHYbJbcNJaQl95bdBq45YGEo-3Q,4246
21
+ karton/core/asyncio/karton.py,sha256=LhzMGuJsmXdTEa323gZted8KgVfHH6l0j0_tTqMh4Z4,12932
22
+ karton/core/asyncio/logger.py,sha256=BjkbuAeWylTmFjWv8-ckmOGf4nL2Tma96W0nIOc2vwk,1752
23
+ karton/core/asyncio/resource.py,sha256=86AYm7JeVjEYRNw--h02HIS9xFvgddhktmDUp0qvTO4,12517
24
+ karton/system/__init__.py,sha256=JF51OqRU_Y4c0unOulvmv1KzSHSq4ZpXU8ZsH4nefRM,63
25
+ karton/system/__main__.py,sha256=QJkwIlSwaPRdzwKlNmCAL41HtDAa73db9MZKWmOfxGM,56
26
+ karton/system/system.py,sha256=d_5hhLTthJdr_4gZEGQ6Y-kHvxeBqyQxjjx_wRs3xMA,17285
27
+ karton_core-5.8.0.dist-info/LICENSE,sha256=o8h7hYhn7BJC_-DmrfqWwLjaR_Gbe0TZOOQJuN2ca3I,1519
28
+ karton_core-5.8.0.dist-info/METADATA,sha256=26n1VrX-0ESRMG7fug5hqKxYT1URIa_NgypF5N1E2gg,6860
29
+ karton_core-5.8.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
30
+ karton_core-5.8.0.dist-info/entry_points.txt,sha256=OgLlsXy61GP6-Yob3oXqeJ2hlRU6LBLj33fr0NufKz0,98
31
+ karton_core-5.8.0.dist-info/namespace_packages.txt,sha256=X8SslCPsqXDCnGZqrYYolzT3xPzJMq1r-ZQSc0jfAEA,7
32
+ karton_core-5.8.0.dist-info/top_level.txt,sha256=X8SslCPsqXDCnGZqrYYolzT3xPzJMq1r-ZQSc0jfAEA,7
33
+ karton_core-5.8.0.dist-info/RECORD,,
@@ -1,27 +0,0 @@
1
- karton_core-5.7.0-nspkg.pth,sha256=vHa-jm6pBTeInFrmnsHMg9AOeD88czzQy-6QCFbpRcM,539
2
- karton/core/__init__.py,sha256=QuT0BWZyp799eY90tK3H1OD2hwuusqMJq8vQwpB3kG4,337
3
- karton/core/__version__.py,sha256=QmHMXVnw5DVPfWzvN7FS1tOhDAesdxpM_aVOh9CMuSk,22
4
- karton/core/backend.py,sha256=_IOjN9pWdSBsDnTMYvg-Fpm6Ag-uf2Jb9LWmrtZqVAU,38773
5
- karton/core/base.py,sha256=lqVJvCHRMzvIOpS8SaWlOaSSJBEVkNQe0oClZC_GQYM,8225
6
- karton/core/config.py,sha256=M3dB0XgnUO5VzUcGyQa7FyKzmdgmDml1MrzG6CxEuvE,8100
7
- karton/core/exceptions.py,sha256=8i9WVzi4PinNlX10Cb-lQQC35Hl-JB5R_UKXa9AUKoQ,153
8
- karton/core/inspect.py,sha256=aIJQEOEkD5q2xLlV8nhxY5qL5zqcnprP-2DdP6ecKlE,6150
9
- karton/core/karton.py,sha256=l3joJWw8m23wlOErkcQmNFYhLFA5x2la6L0WopxJ7mk,15435
10
- karton/core/logger.py,sha256=J3XAyG88U0cwYC9zR6E3QD1uJenrQh7zS9-HgxhqeAs,2040
11
- karton/core/main.py,sha256=ir1-dhn3vbwfh2YHiM6ZYfRBbjwLvJSz0d8tuK1mb_4,8310
12
- karton/core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- karton/core/query.py,sha256=sf24DweVlXfJuBbBD_ns2LXhOV-IBwuPG3jBfTJu77s,12063
14
- karton/core/resource.py,sha256=9kWXpMBRfudH0_whJfSSI27K3Gwv2u93CVa7p68Q5UM,20842
15
- karton/core/task.py,sha256=gW1szMi5PN2Y06X-Ryo7cmEVluZv1r7W5tvmwIJiD94,18808
16
- karton/core/test.py,sha256=cj6W4gNt0BpRjsYiiBt0hPE8dmRfUeIc8sSVkxB50cU,9123
17
- karton/core/utils.py,sha256=sEVqGdVPyYswWuVn8wYXBQmln8Az826N_2HgC__pmW8,4090
18
- karton/system/__init__.py,sha256=JF51OqRU_Y4c0unOulvmv1KzSHSq4ZpXU8ZsH4nefRM,63
19
- karton/system/__main__.py,sha256=QJkwIlSwaPRdzwKlNmCAL41HtDAa73db9MZKWmOfxGM,56
20
- karton/system/system.py,sha256=d_5hhLTthJdr_4gZEGQ6Y-kHvxeBqyQxjjx_wRs3xMA,17285
21
- karton_core-5.7.0.dist-info/LICENSE,sha256=o8h7hYhn7BJC_-DmrfqWwLjaR_Gbe0TZOOQJuN2ca3I,1519
22
- karton_core-5.7.0.dist-info/METADATA,sha256=MrmtycTaYsNB8v0LRyuLIHL2bV17n1Lt6e-ak4RfrH8,6818
23
- karton_core-5.7.0.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
24
- karton_core-5.7.0.dist-info/entry_points.txt,sha256=OgLlsXy61GP6-Yob3oXqeJ2hlRU6LBLj33fr0NufKz0,98
25
- karton_core-5.7.0.dist-info/namespace_packages.txt,sha256=X8SslCPsqXDCnGZqrYYolzT3xPzJMq1r-ZQSc0jfAEA,7
26
- karton_core-5.7.0.dist-info/top_level.txt,sha256=X8SslCPsqXDCnGZqrYYolzT3xPzJMq1r-ZQSc0jfAEA,7
27
- karton_core-5.7.0.dist-info/RECORD,,