atk-common 2.1.0__tar.gz → 3.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.
- {atk_common-2.1.0 → atk_common-3.0.0}/PKG-INFO +1 -1
- {atk_common-2.1.0 → atk_common-3.0.0}/setup.py +1 -1
- atk_common-3.0.0/src/atk_common/__init__.py +1 -0
- atk_common-3.0.0/src/atk_common/classes/__init__.py +16 -0
- atk_common-3.0.0/src/atk_common/classes/bo_logger.py +62 -0
- atk_common-3.0.0/src/atk_common/classes/docker_handler.py +76 -0
- atk_common-3.0.0/src/atk_common/classes/env_handler.py +38 -0
- atk_common-3.0.0/src/atk_common/classes/error_handler.py +81 -0
- atk_common-3.0.0/src/atk_common/classes/http_response_handler.py +42 -0
- atk_common-3.0.0/src/atk_common/classes/rabbitmq_consumer.py +72 -0
- atk_common-3.0.0/src/atk_common/interfaces/__init__.py +14 -0
- atk_common-3.0.0/src/atk_common/interfaces/docker_handler_interface.py +9 -0
- atk_common-3.0.0/src/atk_common/interfaces/env_handler_interface.py +8 -0
- atk_common-3.0.0/src/atk_common/interfaces/error_handler_interface.py +11 -0
- atk_common-3.0.0/src/atk_common/interfaces/http_response_handler_interface.py +5 -0
- atk_common-3.0.0/src/atk_common/interfaces/logger_interface.py +9 -0
- atk_common-3.0.0/src/atk_common/utils/__init__.py +47 -0
- {atk_common-2.1.0/src/atk_common → atk_common-3.0.0/src/atk_common/utils}/consumer_retry_handler.py +21 -15
- {atk_common-2.1.0/src/atk_common → atk_common-3.0.0/src/atk_common/utils}/default_should_retry.py +1 -1
- atk_common-3.0.0/src/atk_common/utils/error_utils.py +5 -0
- {atk_common-2.1.0/src/atk_common → atk_common-3.0.0/src/atk_common/utils}/http_utils.py +1 -1
- atk_common-3.0.0/src/atk_common/utils/mq_utils.py +30 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common.egg-info/PKG-INFO +1 -1
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common.egg-info/SOURCES.txt +24 -16
- {atk_common-2.1.0 → atk_common-3.0.0}/tests/test_docker_utils.py +1 -1
- {atk_common-2.1.0 → atk_common-3.0.0}/tests/test_error_utils.py +1 -1
- {atk_common-2.1.0 → atk_common-3.0.0}/tests/test_http_utils.py +1 -1
- {atk_common-2.1.0 → atk_common-3.0.0}/tests/test_log_utils.py +1 -1
- atk_common-2.1.0/src/atk_common/__init__.py +0 -67
- atk_common-2.1.0/src/atk_common/bo_logger.py +0 -43
- atk_common-2.1.0/src/atk_common/docker_utils.py +0 -70
- atk_common-2.1.0/src/atk_common/env_utils.py +0 -27
- atk_common-2.1.0/src/atk_common/error_utils.py +0 -73
- atk_common-2.1.0/src/atk_common/http_response_utils.py +0 -37
- atk_common-2.1.0/src/atk_common/log_utils.py +0 -16
- atk_common-2.1.0/src/atk_common/mq_utils.py +0 -27
- atk_common-2.1.0/src/atk_common/rabbitmq_consumer.py +0 -51
- {atk_common-2.1.0 → atk_common-3.0.0}/README.md +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/license.txt +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/pyproject.toml +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/setup.cfg +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/__init__.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/api_error_type_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/camera_cabinet_type_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/camera_role_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/certificate_issuer_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/command_status_type_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/command_type_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/config_reply_status_type_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/detection_status_type_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/encryption_type_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/file_exists_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/history_status_type_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/image_encoding_type_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/image_part_category_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/image_part_type_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/image_shelf_type_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/log_level_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/metering_direction_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/multimotor_status_type_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/piezo_vehicle_type_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/process_status_type_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/response_status_type_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/section_role_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/sensor_order_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/sensor_type_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/speed_control_status_type_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/speed_control_stop_reason.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/test_image_type_enum.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common/enums/violation_type_enum.py +0 -0
- {atk_common-2.1.0/src/atk_common → atk_common-3.0.0/src/atk_common/utils}/datetime_utils.py +0 -0
- {atk_common-2.1.0/src/atk_common → atk_common-3.0.0/src/atk_common/utils}/db_utils.py +0 -0
- {atk_common-2.1.0/src/atk_common → atk_common-3.0.0/src/atk_common/utils}/file_utils.py +0 -0
- {atk_common-2.1.0/src/atk_common → atk_common-3.0.0/src/atk_common/utils}/hash_utils.py +0 -0
- {atk_common-2.1.0/src/atk_common → atk_common-3.0.0/src/atk_common/utils}/internal_response_utils.py +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common.egg-info/dependency_links.txt +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common.egg-info/requires.txt +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/atk_common.egg-info/top_level.txt +0 -0
- {atk_common-2.1.0 → atk_common-3.0.0}/src/shared_python_atk_enforcement/__init__.py +0 -0
@@ -36,7 +36,7 @@ setup(
|
|
36
36
|
# For a discussion on single-sourcing the version across setup.py and the
|
37
37
|
# project code, see
|
38
38
|
# https://packaging.python.org/guides/single-sourcing-package-version/
|
39
|
-
version="
|
39
|
+
version="3.0.0", # Required
|
40
40
|
# This is a one-line description or tagline of what your project does. This
|
41
41
|
# corresponds to the "Summary" metadata field:
|
42
42
|
# https://packaging.python.org/specifications/core-metadata/#summary
|
@@ -0,0 +1 @@
|
|
1
|
+
# __init__.py
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# __init__.py
|
2
|
+
from atk_common.classes.bo_logger import BoLogger
|
3
|
+
from atk_common.classes.docker_handler import DockerHandler
|
4
|
+
from atk_common.classes.env_handler import EnvHandler
|
5
|
+
from atk_common.classes.error_handler import ErrorHandler
|
6
|
+
from atk_common.classes.http_response_handler import HttpResponseHandler
|
7
|
+
from atk_common.classes.rabbitmq_consumer import RabbitMQConsumer
|
8
|
+
|
9
|
+
__all__ = [
|
10
|
+
'BoLogger',
|
11
|
+
'DockerHandler',
|
12
|
+
'EnvHandler',
|
13
|
+
'ErrorHandler',
|
14
|
+
'HttpResponseHandler',
|
15
|
+
'RabbitMQConsumer',
|
16
|
+
]
|
@@ -0,0 +1,62 @@
|
|
1
|
+
import json
|
2
|
+
from typing import Optional
|
3
|
+
from opentelemetry.trace import get_current_span
|
4
|
+
from atk_common.interfaces import ILogger # for type hints only
|
5
|
+
from atk_common.enums import LogLevel
|
6
|
+
from atk_common.utils.datetime_utils import get_utc_date_time_str
|
7
|
+
from atk_common.enums.log_level_enum import LogLevel
|
8
|
+
|
9
|
+
class BoLogger(ILogger):
|
10
|
+
def __init__(self, log_level: LogLevel, component, version):
|
11
|
+
if log_level is None:
|
12
|
+
self.log_level = LogLevel.INFO
|
13
|
+
else:
|
14
|
+
self.log_level = log_level
|
15
|
+
self.component = component
|
16
|
+
self.version = version
|
17
|
+
|
18
|
+
def _get_trace_context(self):
|
19
|
+
span = get_current_span()
|
20
|
+
ctx = span.get_span_context()
|
21
|
+
if ctx.is_valid:
|
22
|
+
return {
|
23
|
+
"trace_id": f"{ctx.trace_id:032x}",
|
24
|
+
"span_id": f"{ctx.span_id:016x}",
|
25
|
+
}
|
26
|
+
return {"trace_id": None, "span_id": None}
|
27
|
+
|
28
|
+
def _create_log_json(self, timestamp, level: LogLevel, message: str):
|
29
|
+
log_entry = {
|
30
|
+
"timestamp": timestamp,
|
31
|
+
"severity": level.name,
|
32
|
+
"message": message,
|
33
|
+
"component": self.component,
|
34
|
+
"version": self.version,
|
35
|
+
}
|
36
|
+
log_entry.update(self._get_trace_context())
|
37
|
+
return log_entry
|
38
|
+
|
39
|
+
def _log(self, level: LogLevel, message: str):
|
40
|
+
if level.value >= self.log_level.value:
|
41
|
+
timestamp = get_utc_date_time_str()
|
42
|
+
log_json = self._create_log_json(timestamp, level, message)
|
43
|
+
print(json.dumps(log_json))
|
44
|
+
|
45
|
+
def debug(self, message: str):
|
46
|
+
self._log(LogLevel.DEBUG, message)
|
47
|
+
|
48
|
+
def info(self, message: str):
|
49
|
+
self._log(LogLevel.INFO, message)
|
50
|
+
|
51
|
+
def warning(self, message: str):
|
52
|
+
self._log(LogLevel.WARNING, message)
|
53
|
+
|
54
|
+
def error(self, message: str):
|
55
|
+
self._log(LogLevel.ERROR, message)
|
56
|
+
|
57
|
+
def critical(self, message: str):
|
58
|
+
self._log(LogLevel.CRITICAL, message)
|
59
|
+
|
60
|
+
def set_level(self, log_level: LogLevel):
|
61
|
+
self.log_level = log_level
|
62
|
+
|
@@ -0,0 +1,76 @@
|
|
1
|
+
import docker
|
2
|
+
import socket
|
3
|
+
from atk_common.interfaces import IDockerHandler
|
4
|
+
from atk_common.interfaces import ILogger
|
5
|
+
|
6
|
+
class DockerHandler(IDockerHandler):
|
7
|
+
def __init__(self, logger: ILogger, image_name, image_version):
|
8
|
+
self.logger = logger
|
9
|
+
self.image_name = image_name
|
10
|
+
self.image_version = image_version
|
11
|
+
self.set_container_metadata()
|
12
|
+
|
13
|
+
def get_image_name_and_version(self, tags):
|
14
|
+
if tags:
|
15
|
+
# Use the first tag (usually only one)
|
16
|
+
full_tag = tags[0] # e.g., 'bo-crypto-wrapper-api:latest'
|
17
|
+
|
18
|
+
if ":" in full_tag:
|
19
|
+
image_name, image_version = full_tag.split(":", 1)
|
20
|
+
else:
|
21
|
+
image_name = full_tag
|
22
|
+
image_version = "<none>"
|
23
|
+
return image_name, image_version
|
24
|
+
else:
|
25
|
+
return None, None
|
26
|
+
|
27
|
+
def create_port_item(self, port, binding):
|
28
|
+
data = {}
|
29
|
+
data['port'] = port
|
30
|
+
data['binding'] = binding
|
31
|
+
return data
|
32
|
+
|
33
|
+
def create_container_log(self, container_data):
|
34
|
+
image_name = container_data.get('imageName') or 'Unknown'
|
35
|
+
image_version = container_data.get('imageVersion') or 'Unknown'
|
36
|
+
|
37
|
+
log_str = f'Image name: {image_name}, image version: {image_version}'
|
38
|
+
self.logger.info(log_str)
|
39
|
+
|
40
|
+
def get_current_container_info(self):
|
41
|
+
try:
|
42
|
+
data = {}
|
43
|
+
client = docker.from_env()
|
44
|
+
|
45
|
+
# Get current container's hostname (usually the container ID)
|
46
|
+
container_id = socket.gethostname()
|
47
|
+
|
48
|
+
# Fetch container object using partial ID
|
49
|
+
container = client.containers.get(container_id)
|
50
|
+
|
51
|
+
tags_info = self.get_image_name_and_version(container.image.tags)
|
52
|
+
data['imageName'] = tags_info[0]
|
53
|
+
data['imageVersion'] = tags_info[1]
|
54
|
+
data['containerName'] = container.name
|
55
|
+
ports = container.attrs['NetworkSettings']['Ports']
|
56
|
+
data['ports'] = []
|
57
|
+
if ports:
|
58
|
+
for container_port, host_bindings in ports.items():
|
59
|
+
if host_bindings:
|
60
|
+
for binding in host_bindings:
|
61
|
+
data['ports'].append(self.create_port_item(container_port, f"{binding['HostIp']}:{binding['HostPort']}"))
|
62
|
+
else:
|
63
|
+
data['ports'].append(self.create_port_item(container_port, None))
|
64
|
+
self.create_container_log(data)
|
65
|
+
return data
|
66
|
+
except Exception as e:
|
67
|
+
self.logger.error("Error getting container data:" + str(e))
|
68
|
+
return None
|
69
|
+
|
70
|
+
def set_container_metadata(self):
|
71
|
+
data = {}
|
72
|
+
data['imageName'] = self.image_name
|
73
|
+
data['imageVersion'] = self.image_version
|
74
|
+
data['containerName'] = self.image_name
|
75
|
+
self.create_container_log(data)
|
76
|
+
return data
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import os
|
2
|
+
from typing import Optional
|
3
|
+
from atk_common.interfaces import IEnvHandler
|
4
|
+
from atk_common.interfaces import ILogger
|
5
|
+
|
6
|
+
class EnvHandler(IEnvHandler):
|
7
|
+
def __init__(self, logger: Optional(ILogger)): # type: ignore
|
8
|
+
self.logger = logger
|
9
|
+
|
10
|
+
def val_str(self, value):
|
11
|
+
if value is None:
|
12
|
+
return '<Empty>'
|
13
|
+
if isinstance(value, str):
|
14
|
+
if value.strip() == '' or value.lower() == 'null':
|
15
|
+
return '<Null>'
|
16
|
+
return value
|
17
|
+
return str(value)
|
18
|
+
|
19
|
+
def is_value_null_or_empty(self, value):
|
20
|
+
if isinstance(value, str):
|
21
|
+
return value.strip() == '' or value.lower() == 'null'
|
22
|
+
return False
|
23
|
+
|
24
|
+
def get_env_value(self, key, abort_on_error=True):
|
25
|
+
val = os.environ.get(key)
|
26
|
+
if self.logger:
|
27
|
+
self.logger.info(key + ':' + self.val_str(val))
|
28
|
+
if val is None and abort_on_error:
|
29
|
+
err_msg = f"Environment variable '{key}' is not set."
|
30
|
+
if self.logger:
|
31
|
+
self.logger.critical(err_msg)
|
32
|
+
raise ValueError(err_msg)
|
33
|
+
if self.is_value_null_or_empty(val):
|
34
|
+
return None
|
35
|
+
return val
|
36
|
+
|
37
|
+
def set_logger(self, logger: ILogger):
|
38
|
+
self.logger = logger
|
@@ -0,0 +1,81 @@
|
|
1
|
+
from http import HTTPStatus
|
2
|
+
from flask import Response
|
3
|
+
from enum import Enum
|
4
|
+
import json
|
5
|
+
from typing import Optional
|
6
|
+
from atk_common.utils.datetime_utils import get_utc_date_time_str
|
7
|
+
from atk_common.enums.api_error_type_enum import ApiErrorType
|
8
|
+
from atk_common.utils.http_utils import is_http_status_internal
|
9
|
+
from atk_common.utils.internal_response_utils import create_response
|
10
|
+
from atk_common.interfaces import IErrorHandler
|
11
|
+
from atk_common.interfaces import ILogger
|
12
|
+
from atk_common.interfaces import IDockerHandler
|
13
|
+
|
14
|
+
class ErrorHandler(IErrorHandler):
|
15
|
+
def __init__(self, logger: ILogger, docker_handler: IDockerHandler):
|
16
|
+
self.logger = logger
|
17
|
+
self.docker_handler = docker_handler
|
18
|
+
|
19
|
+
def get_message(self, error):
|
20
|
+
if hasattr(error, 'message'):
|
21
|
+
return str(error.message)
|
22
|
+
else:
|
23
|
+
return str(error)
|
24
|
+
|
25
|
+
def create_error_log(self, data):
|
26
|
+
err_str = data['message'] + ', statusCode: ' + str(data['statusCode']) + ', method: ' + data['method']
|
27
|
+
if data['containerInfo'] is not None:
|
28
|
+
err_str += ', imageName: ' + data['containerInfo']['imageName']
|
29
|
+
err_str += ', imageVersion: ' + data['containerInfo']['imageVersion']
|
30
|
+
err_str += ', containerName: ' + data['containerInfo']['containerName']
|
31
|
+
else:
|
32
|
+
err_str += ', imageName: <none>'
|
33
|
+
err_str += ', imageVersion: <none>'
|
34
|
+
err_str += ', containerName: <none>'
|
35
|
+
self.logger.critical(err_str)
|
36
|
+
|
37
|
+
def get_error_entity(self, error, method, error_type, status_code, container_info):
|
38
|
+
data = {}
|
39
|
+
data['statusCode'] = status_code
|
40
|
+
data['exceptionType'] = str(type(error))
|
41
|
+
data['errorType'] = error_type.value if isinstance(error_type, Enum) else error_type
|
42
|
+
data['message'] = self.get_message(error)
|
43
|
+
data['method'] = method
|
44
|
+
data['timestamp'] = get_utc_date_time_str()
|
45
|
+
data['containerInfo'] = container_info
|
46
|
+
self.create_error_log(data)
|
47
|
+
return Response(
|
48
|
+
response=json.dumps(data),
|
49
|
+
status=HTTPStatus.INTERNAL_SERVER_ERROR,
|
50
|
+
mimetype='application/json'
|
51
|
+
)
|
52
|
+
|
53
|
+
def resend_error_entity(self, error_entity):
|
54
|
+
self.create_error_log(error_entity)
|
55
|
+
return Response(
|
56
|
+
response=json.dumps(error_entity),
|
57
|
+
status=HTTPStatus.INTERNAL_SERVER_ERROR,
|
58
|
+
mimetype='application/json'
|
59
|
+
)
|
60
|
+
|
61
|
+
def handle_error(self, resp, status):
|
62
|
+
if is_http_status_internal(resp.status_code):
|
63
|
+
self.logger.info(resp.json().get('message'))
|
64
|
+
return create_response(status, resp.status_code, resp.json())
|
65
|
+
else:
|
66
|
+
self.logger.info(resp.text)
|
67
|
+
return create_response(status, resp.status_code, resp.text)
|
68
|
+
|
69
|
+
def get_response_error(self, resp):
|
70
|
+
if is_http_status_internal(resp.status_code):
|
71
|
+
return resp.json()
|
72
|
+
else:
|
73
|
+
return resp.text
|
74
|
+
|
75
|
+
# Return values:
|
76
|
+
# 1 - Connection error
|
77
|
+
# 2 - Internal error
|
78
|
+
def get_error_type(self, conn):
|
79
|
+
if conn is None:
|
80
|
+
return ApiErrorType.CONNECTION
|
81
|
+
return ApiErrorType.INTERNAL
|
@@ -0,0 +1,42 @@
|
|
1
|
+
import json
|
2
|
+
from flask import Response
|
3
|
+
from http import HTTPStatus
|
4
|
+
from atk_common.enums.api_error_type_enum import ApiErrorType
|
5
|
+
from atk_common.utils.http_utils import is_http_status_internal, is_http_status_ok
|
6
|
+
from atk_common.utils.internal_response_utils import is_response_http
|
7
|
+
from atk_common.interfaces import IErrorHandler
|
8
|
+
from atk_common.interfaces import IHttpResponseHandler
|
9
|
+
|
10
|
+
class HttpResponseHandler(IHttpResponseHandler):
|
11
|
+
def __init__(self, error_handler: IErrorHandler):
|
12
|
+
self.error_handler = error_handler
|
13
|
+
|
14
|
+
def _convert_response_data(self, data):
|
15
|
+
if isinstance(data, dict):
|
16
|
+
return json.dumps(data)
|
17
|
+
elif isinstance(data, list):
|
18
|
+
return json.dumps(data)
|
19
|
+
elif isinstance(data, str):
|
20
|
+
json_data = json.loads(data)
|
21
|
+
return json.dumps(json_data)
|
22
|
+
else:
|
23
|
+
return data
|
24
|
+
|
25
|
+
# If response['status'] == 0 (OK, http status = 200): create Response and return response['responseMsg']
|
26
|
+
# If http status == 500:
|
27
|
+
# If response['status'] == 1 (HTTP): resend received error entity
|
28
|
+
# If response['status'] == 2 (INTERNAL): create new error entity and return as response
|
29
|
+
# If http status other value: create new error entity and return as response
|
30
|
+
def http_response(self, method, response, container_info):
|
31
|
+
if is_http_status_ok(response['statusCode']):
|
32
|
+
return Response(
|
33
|
+
response=self._convert_response_data(response['responseMsg']),
|
34
|
+
status=HTTPStatus.OK,
|
35
|
+
mimetype=response['contentType'],
|
36
|
+
headers=response['httpHeaders']
|
37
|
+
)
|
38
|
+
elif is_http_status_internal(response['statusCode']):
|
39
|
+
if is_response_http(response):
|
40
|
+
return self.error_handler.resend_error_entity(response['responseMsg'])
|
41
|
+
return self.error_handler.get_error_entity(response['responseMsg'], method, ApiErrorType.INTERNAL, response['statusCode'], container_info)
|
42
|
+
return self.error_handler.get_error_entity(response['responseMsg'], method, ApiErrorType.CONNECTION, response['statusCode'], container_info)
|
@@ -0,0 +1,72 @@
|
|
1
|
+
from kombu import Connection, Queue, Consumer
|
2
|
+
from kombu.exceptions import ConnectionError, ChannelError # Import relevant exceptions
|
3
|
+
import socket
|
4
|
+
import time
|
5
|
+
from atk_common.interfaces import ILogger
|
6
|
+
CONNECTION_ERRORS = (
|
7
|
+
ConnectionError,
|
8
|
+
ChannelError,
|
9
|
+
OSError,
|
10
|
+
)
|
11
|
+
|
12
|
+
class RabbitMQConsumer:
|
13
|
+
def __init__(self, queue_name, user, pwd, host, vhost, dlx, dlq, content_type, message_handler, logger: ILogger):
|
14
|
+
self.logger = logger
|
15
|
+
|
16
|
+
rabbit_url = 'amqp://' + user + ':' + pwd + '@' + host + '/' + vhost
|
17
|
+
self.logger.info(f"Initializing connection to RabbitMQ {queue_name} at {host}")
|
18
|
+
|
19
|
+
self.connection = Connection(rabbit_url, heartbeat=10)
|
20
|
+
if dlx is not None and dlq is not None:
|
21
|
+
queue = Queue(name=queue_name,
|
22
|
+
queue_arguments={
|
23
|
+
'x-dead-letter-exchange': dlx,
|
24
|
+
'x-dead-letter-routing-key': dlq},
|
25
|
+
)
|
26
|
+
else:
|
27
|
+
queue = Queue(name=queue_name)
|
28
|
+
self.queue = queue
|
29
|
+
self.content_type = content_type # Optional content type for message handling
|
30
|
+
self.message_handler = message_handler # Custom message handler
|
31
|
+
|
32
|
+
def consume(self):
|
33
|
+
conn = self.establish_connection()
|
34
|
+
self.logger.info("Begin consuming messages...")
|
35
|
+
|
36
|
+
while True:
|
37
|
+
try:
|
38
|
+
conn.drain_events(timeout=2)
|
39
|
+
self.logger.debug("Drained event or heartbeat.")
|
40
|
+
except socket.timeout:
|
41
|
+
self.logger.debug("Socket timeout, checking heartbeat...")
|
42
|
+
conn.heartbeat_check()
|
43
|
+
except CONNECTION_ERRORS as e:
|
44
|
+
self.logger.critical(f"Connection lost: {e}. Reconnecting...")
|
45
|
+
return # break loop and re-establish connection
|
46
|
+
except Exception as e:
|
47
|
+
self.logger.critical(f"Top-level exception in consume loop: {e}. Restarting after delay...")
|
48
|
+
return
|
49
|
+
|
50
|
+
def establish_connection(self):
|
51
|
+
revived_connection = self.connection.clone()
|
52
|
+
revived_connection.ensure_connection(max_retries=3)
|
53
|
+
channel = revived_connection.channel()
|
54
|
+
|
55
|
+
consumer = Consumer(
|
56
|
+
revived_connection,
|
57
|
+
queues=self.queue,
|
58
|
+
callbacks=[self.message_handler],
|
59
|
+
accept=[self.content_type] if self.content_type else None)
|
60
|
+
consumer.revive(channel)
|
61
|
+
consumer.consume()
|
62
|
+
self.logger.info("Connection revived!")
|
63
|
+
return revived_connection
|
64
|
+
|
65
|
+
def run(self):
|
66
|
+
self.logger.info("Starting RabbitMQ consumer run loop...")
|
67
|
+
while True:
|
68
|
+
try:
|
69
|
+
self.consume()
|
70
|
+
except Exception as e:
|
71
|
+
self.logger.critical(f"Top-level exception in run loop: {e}. Restarting after delay...")
|
72
|
+
time.sleep(5)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# __init__.py
|
2
|
+
from atk_common.interfaces.docker_handler_interface import IDockerHandler
|
3
|
+
from atk_common.interfaces.env_handler_interface import IEnvHandler
|
4
|
+
from atk_common.interfaces.error_handler_interface import IErrorHandler
|
5
|
+
from atk_common.interfaces.http_response_handler_interface import IHttpResponseHandler
|
6
|
+
from atk_common.interfaces.logger_interface import ILogger
|
7
|
+
|
8
|
+
__all__ = [
|
9
|
+
'IDockerHandler',
|
10
|
+
'IEnvHandler',
|
11
|
+
'IErrorHandler',
|
12
|
+
'ILogger',
|
13
|
+
'IHttpResponseHandler',
|
14
|
+
]
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# interfaces/logger_interface.py
|
2
|
+
from typing import Protocol
|
3
|
+
|
4
|
+
class IDockerHandler(Protocol):
|
5
|
+
def get_image_name_and_version(self, tags) -> None: ...
|
6
|
+
def create_port_item(port, binding) -> None: ...
|
7
|
+
def create_container_log(container_data) -> None: ...
|
8
|
+
def get_current_container_info() -> None: ...
|
9
|
+
def set_container_metadata(image_name_env_key, image_version_env_key) -> None: ...
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# interfaces/env_handler_interface.py
|
2
|
+
from typing import Protocol
|
3
|
+
|
4
|
+
class IEnvHandler(Protocol):
|
5
|
+
def val_str(self, value) -> None: ...
|
6
|
+
def is_value_null_or_empty(self, value) -> None: ...
|
7
|
+
def get_env_value(self, msg: str) -> None: ...
|
8
|
+
def set_logger(self, logger) -> None: ...
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# interfaces/error_handler_interface.py
|
2
|
+
from typing import Protocol
|
3
|
+
|
4
|
+
class IErrorHandler(Protocol):
|
5
|
+
def get_message(self, error) -> None: ...
|
6
|
+
def create_error_log(self, data) -> None: ...
|
7
|
+
def get_error_entity(self, error, method, error_type, status_code, container_info) -> None: ...
|
8
|
+
def resend_error_entity(self, error_entity) -> None: ...
|
9
|
+
def handle_error(self, resp, status) -> None: ...
|
10
|
+
def get_response_error(self, resp) -> None: ...
|
11
|
+
def get_error_type(self, conn) -> None: ...
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# interfaces/logger_interface.py
|
2
|
+
from typing import Protocol
|
3
|
+
|
4
|
+
class ILogger(Protocol):
|
5
|
+
def debug(self, msg: str) -> None: ...
|
6
|
+
def info(self, msg: str) -> None: ...
|
7
|
+
def warning(self, msg: str) -> None: ...
|
8
|
+
def error(self, msg: str) -> None: ...
|
9
|
+
def critical(self, msg: str) -> None: ...
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# __init__.py
|
2
|
+
from atk_common.utils.consumer_retry_handler import create_retry_handler
|
3
|
+
from atk_common.utils.datetime_utils import \
|
4
|
+
get_utc_date_time, \
|
5
|
+
get_utc_date_time_str, \
|
6
|
+
get_utc_date_time_str_with_z, \
|
7
|
+
seconds_to_utc_timestamp, \
|
8
|
+
get_utc_date_from_iso, \
|
9
|
+
adjust_millisescond, \
|
10
|
+
convert_to_utc, \
|
11
|
+
convert_to_utc_image_dt
|
12
|
+
from atk_common.utils.db_utils import sql, sql_with_record, convert_none_to_null, date_time_utc_column
|
13
|
+
from atk_common.utils.default_should_retry import default_should_retry
|
14
|
+
from atk_common.utils.error_utils import get_message
|
15
|
+
from atk_common.utils.hash_utils import create_enforcement_hash
|
16
|
+
from atk_common.utils.http_utils import is_http_status_ok, is_http_status_internal, get_test_response
|
17
|
+
from atk_common.utils.internal_response_utils import create_response, is_response_ok, is_response_http, is_response_internal
|
18
|
+
from atk_common.utils.file_utils import get_image_file_type
|
19
|
+
from atk_common.utils.mq_utils import decode_message
|
20
|
+
|
21
|
+
__all__ = [
|
22
|
+
'create_retry_handler',
|
23
|
+
'get_utc_date_time',
|
24
|
+
'get_utc_date_time_str',
|
25
|
+
'get_utc_date_time_str_with_z',
|
26
|
+
'seconds_to_utc_timestamp',
|
27
|
+
'get_utc_date_from_iso',
|
28
|
+
'adjust_millisescond',
|
29
|
+
'convert_to_utc',
|
30
|
+
'convert_to_utc_image_dt',
|
31
|
+
'sql',
|
32
|
+
'sql_with_record',
|
33
|
+
'convert_none_to_null',
|
34
|
+
'date_time_utc_column',
|
35
|
+
'default_should_retry',
|
36
|
+
'get_message',
|
37
|
+
'create_enforcement_hash',
|
38
|
+
'is_http_status_ok',
|
39
|
+
'is_http_status_internal',
|
40
|
+
'get_test_response',
|
41
|
+
'create_response',
|
42
|
+
'is_response_ok',
|
43
|
+
'is_response_http',
|
44
|
+
'is_response_internal',
|
45
|
+
'get_image_file_type',
|
46
|
+
'decode_message',
|
47
|
+
]
|
{atk_common-2.1.0/src/atk_common → atk_common-3.0.0/src/atk_common/utils}/consumer_retry_handler.py
RENAMED
@@ -1,13 +1,17 @@
|
|
1
1
|
from kombu import Producer
|
2
|
-
from atk_common.
|
2
|
+
from atk_common.classes.bo_logger import BoLogger
|
3
|
+
from atk_common.utils.internal_response_utils import is_response_ok
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
"""
|
6
|
+
process_func: your original handler function (body, message)
|
7
|
+
connection: kombu.Connection instance to use for retrying (nullable)
|
8
|
+
exchange: kombu.Exchange instance to publish to (nullable)
|
9
|
+
routing_key: routing key for the exchange (nullable)
|
10
|
+
should_retry: function(message_status) -> bool
|
11
|
+
bo_logger: BoLogger instance for logging
|
12
|
+
declare: optional kombu.DeclarativeExchange or Queue to declare before publishing
|
13
|
+
"""
|
14
|
+
def create_retry_handler(process_func, connection, exchange, routing_key, should_retry, bo_logger: BoLogger, declare=None):
|
11
15
|
|
12
16
|
def handler(body, message):
|
13
17
|
try:
|
@@ -18,7 +22,7 @@ def create_retry_handler(process_func, connection, exchange, routing_key, should
|
|
18
22
|
if connection is not None:
|
19
23
|
# Use retry queue
|
20
24
|
if should_retry(process_response):
|
21
|
-
|
25
|
+
bo_logger.info("Retrying after delay...")
|
22
26
|
with connection.Producer() as producer:
|
23
27
|
producer.publish(
|
24
28
|
message.body,
|
@@ -33,17 +37,19 @@ def create_retry_handler(process_func, connection, exchange, routing_key, should
|
|
33
37
|
)
|
34
38
|
message.ack()
|
35
39
|
else:
|
36
|
-
|
40
|
+
bo_logger.critical("Sending to DLQ...")
|
37
41
|
message.reject(requeue=False)
|
38
42
|
else:
|
39
43
|
if should_retry(process_response):
|
40
|
-
|
41
|
-
message.requeue()
|
42
|
-
else:
|
43
|
-
log("Sending to DLQ...")
|
44
|
+
bo_logger.critical("Sending to DLQ...")
|
44
45
|
message.reject(requeue=False)
|
46
|
+
# bo_logger.critical("Requing...")
|
47
|
+
# message.requeue()
|
48
|
+
else:
|
49
|
+
bo_logger.critical("Discarding message...")
|
50
|
+
message.ack()
|
45
51
|
except Exception as e:
|
46
|
-
|
52
|
+
bo_logger.critical(f"Error during processing: {e}, sending to DLQ...")
|
47
53
|
message.reject(requeue=False)
|
48
54
|
|
49
55
|
return handler
|
{atk_common-2.1.0/src/atk_common → atk_common-3.0.0/src/atk_common/utils}/default_should_retry.py
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
from atk_common.enums.api_error_type_enum import ApiErrorType
|
2
2
|
from atk_common.enums.response_status_type_enum import ResponseStatusType
|
3
|
-
from atk_common.http_utils import is_http_status_internal, is_http_status_ok
|
3
|
+
from atk_common.utils.http_utils import is_http_status_internal, is_http_status_ok
|
4
4
|
|
5
5
|
def default_should_retry(message_status):
|
6
6
|
if message_status['status'] == ResponseStatusType.HTTP:
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from http import HTTPStatus
|
2
|
-
from atk_common.datetime_utils import get_utc_date_time_str
|
2
|
+
from atk_common.utils.datetime_utils import get_utc_date_time_str
|
3
3
|
|
4
4
|
def is_http_status_ok(status_code):
|
5
5
|
return status_code >= HTTPStatus.OK.value and status_code < HTTPStatus.MULTIPLE_CHOICES.value
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import json
|
2
|
+
from http import HTTPStatus
|
3
|
+
from atk_common.classes.bo_logger import BoLogger
|
4
|
+
from atk_common.enums.response_status_type_enum import ResponseStatusType
|
5
|
+
from atk_common.utils.error_utils import get_message
|
6
|
+
from atk_common.utils.internal_response_utils import create_response
|
7
|
+
|
8
|
+
def decode_message(body, message, bo_logger: BoLogger):
|
9
|
+
import gzip
|
10
|
+
import msgpack
|
11
|
+
try:
|
12
|
+
content_encoding = message.headers.get('content_encoding')
|
13
|
+
if content_encoding is not None and content_encoding == 'gzip':
|
14
|
+
body = gzip.decompress(body)
|
15
|
+
if message.content_type is None or message.content_type == '':
|
16
|
+
return create_response(ResponseStatusType.OK, HTTPStatus.OK, body)
|
17
|
+
elif message.content_type == 'application/json':
|
18
|
+
return create_response(ResponseStatusType.OK, HTTPStatus.OK, body)
|
19
|
+
elif message.content_type == 'application/octet-stream':
|
20
|
+
return create_response(ResponseStatusType.OK, HTTPStatus.OK, body)
|
21
|
+
elif message.content_type == 'application/x-msgpack' or message.content_type == 'application/msgpack':
|
22
|
+
return create_response(ResponseStatusType.OK, HTTPStatus.OK, msgpack.unpackb(body, raw=False))
|
23
|
+
elif message.content_type.startswith('text/'):
|
24
|
+
return create_response(ResponseStatusType.OK, HTTPStatus.OK, body.decode('utf-8'))
|
25
|
+
else:
|
26
|
+
bo_logger.critical(f"Unknown message content type {message.content_type}. Cannot decode message.")
|
27
|
+
return create_response(ResponseStatusType.INTERNAL, HTTPStatus.INTERNAL_SERVER_ERROR, get_message(error))
|
28
|
+
except Exception as error:
|
29
|
+
bo_logger.critical('Error decoding message: ' + get_message(error))
|
30
|
+
return create_response(ResponseStatusType.INTERNAL, HTTPStatus.INTERNAL_SERVER_ERROR, get_message(error))
|