dt-extensions-sdk 1.2.14__py3-none-any.whl → 1.3.1__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.
- {dt_extensions_sdk-1.2.14.dist-info → dt_extensions_sdk-1.3.1.dist-info}/METADATA +2 -2
- {dt_extensions_sdk-1.2.14.dist-info → dt_extensions_sdk-1.3.1.dist-info}/RECORD +13 -12
- {dt_extensions_sdk-1.2.14.dist-info → dt_extensions_sdk-1.3.1.dist-info}/WHEEL +1 -1
- dynatrace_extension/__about__.py +1 -1
- dynatrace_extension/cli/create/extension_template/.gitignore.template +3 -0
- dynatrace_extension/cli/create/extension_template/activation.json.template +1 -1
- dynatrace_extension/cli/create/extension_template/secrets.json.template +3 -0
- dynatrace_extension/cli/main.py +11 -1
- dynatrace_extension/sdk/callback.py +8 -3
- dynatrace_extension/sdk/communication.py +40 -1
- dynatrace_extension/sdk/extension.py +8 -2
- {dt_extensions_sdk-1.2.14.dist-info → dt_extensions_sdk-1.3.1.dist-info}/entry_points.txt +0 -0
- {dt_extensions_sdk-1.2.14.dist-info → dt_extensions_sdk-1.3.1.dist-info}/licenses/LICENSE.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: dt-extensions-sdk
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.3.1
|
4
4
|
Project-URL: Documentation, https://github.com/dynatrace-extensions/dt-extensions-python-sdk#readme
|
5
5
|
Project-URL: Issues, https://github.com/dynatrace-extensions/dt-extensions-python-sdk/issues
|
6
6
|
Project-URL: Source, https://github.com/dynatrace-extensions/dt-extensions-python-sdk
|
@@ -1,13 +1,14 @@
|
|
1
|
-
dynatrace_extension/__about__.py,sha256
|
1
|
+
dynatrace_extension/__about__.py,sha256=-jEpNU1eRpe8GBW_KoSO-6Flc820P-KhyssXl0K9kO4,110
|
2
2
|
dynatrace_extension/__init__.py,sha256=BvQuknmA7ti3WJi3zEXZfY7aAxJrie37VNitWICsUvI,752
|
3
3
|
dynatrace_extension/cli/__init__.py,sha256=HCboY_eJPoqjFmoPDsBL8Jk6aNvank8K7JpkVrgwzUM,123
|
4
|
-
dynatrace_extension/cli/main.py,sha256=
|
4
|
+
dynatrace_extension/cli/main.py,sha256=Z8gFcp0vIMBkzV6jUd5mwvP1U0JcLqAHoMLJw_6puz4,18260
|
5
5
|
dynatrace_extension/cli/schema.py,sha256=d8wKUodRiaU3hfSZDWVNpD15lBfhmif2oQ-k07IxcaA,3230
|
6
6
|
dynatrace_extension/cli/create/__init__.py,sha256=NfyOJCVlxs8dYtfDAMHS1Q5SJTuZcFzOg5rtaI-ZPRE,72
|
7
7
|
dynatrace_extension/cli/create/create.py,sha256=apXden2M93MDDDm7aa-Os-AEtUtyKbk_PsS56j32NK4,2708
|
8
|
-
dynatrace_extension/cli/create/extension_template/.gitignore.template,sha256=
|
8
|
+
dynatrace_extension/cli/create/extension_template/.gitignore.template,sha256=FPye23W8dqmked4HQBCDCAKFf1UbBGkwhKjQpgmXhdg,3101
|
9
9
|
dynatrace_extension/cli/create/extension_template/README.md.template,sha256=QcV0fYqJ1PHaouKdGQgzveJY5zAFBSICp7xSfgnoQj0,637
|
10
|
-
dynatrace_extension/cli/create/extension_template/activation.json.template,sha256=
|
10
|
+
dynatrace_extension/cli/create/extension_template/activation.json.template,sha256=qX-Fgq_JhUWNYMe1-RMcjwQPzF4scJYGfGlBr043UiY,266
|
11
|
+
dynatrace_extension/cli/create/extension_template/secrets.json.template,sha256=fr-ya8lR0TiUmr6lf-PFko6RZw6sr54FxtMbqIWBVkA,36
|
11
12
|
dynatrace_extension/cli/create/extension_template/setup.py.template,sha256=M4HPg3UFkvmWFHAz6vHbDG5Ocb-ZiR0TQuLt9eedW1M,846
|
12
13
|
dynatrace_extension/cli/create/extension_template/extension/activationSchema.json.template,sha256=me3DL_Q449q4VaOStTqaBL-gUkKnlafC8L2093O0LOY,2905
|
13
14
|
dynatrace_extension/cli/create/extension_template/extension/extension.yaml.template,sha256=qDyAURGXKsZBIPjW2s1BPhD-xOC8_FoBgA33HbsOkAU,299
|
@@ -15,10 +16,10 @@ dynatrace_extension/cli/create/extension_template/extension_name/__init__.py.tem
|
|
15
16
|
dynatrace_extension/cli/create/extension_template/extension_name/__main__.py.template,sha256=NYuZ6BWZ6HOs4TCuboh6S0nuYp3F8MXW2lmjSpdREEA,1205
|
16
17
|
dynatrace_extension/sdk/__init__.py,sha256=RsqQ1heGyCmSK3fhuEKAcxQIRCg4gEK0-eSkIehL5Nc,86
|
17
18
|
dynatrace_extension/sdk/activation.py,sha256=goTbT1tD2kn8xfyXFdTy_cTZNcFPJpgbvQM8HOzKECA,1480
|
18
|
-
dynatrace_extension/sdk/callback.py,sha256=
|
19
|
-
dynatrace_extension/sdk/communication.py,sha256=
|
19
|
+
dynatrace_extension/sdk/callback.py,sha256=eMpC0F3fCI82mWHIFgmy9QmKl7Kq_9dSaanHdV6o7hA,6511
|
20
|
+
dynatrace_extension/sdk/communication.py,sha256=QkJgEBblOen-jmvsb3ZfYZYglYUc7jHbkEgPtRj9l6w,19123
|
20
21
|
dynatrace_extension/sdk/event.py,sha256=J261imbFKpxfuAQ6Nfu3RRcsIQKKivy6fme1nww2g-8,388
|
21
|
-
dynatrace_extension/sdk/extension.py,sha256=
|
22
|
+
dynatrace_extension/sdk/extension.py,sha256=RnF7Czlmg6QrQLLlP90dM0tAIxdX3LrqaUi8M1p1Guo,43251
|
22
23
|
dynatrace_extension/sdk/helper.py,sha256=ZNrO9ao2hE3KQ934vAYD74k0fCr6QTG-_bAvbk9-hi8,6562
|
23
24
|
dynatrace_extension/sdk/metric.py,sha256=7VClzJCFJNDCxA-d69uTu1pdPtDZBTwq7fbafs_L6nQ,3690
|
24
25
|
dynatrace_extension/sdk/runtime.py,sha256=jyYsM1x-gMnW68eWq8IoZZZBarHgIcr_nVeGDDgpRDk,2802
|
@@ -27,8 +28,8 @@ dynatrace_extension/sdk/vendor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5
|
|
27
28
|
dynatrace_extension/sdk/vendor/mureq/LICENSE,sha256=8AVcgZgiT_mvK1fOofXtRRr2f1dRXS_K21NuxQgP4VM,671
|
28
29
|
dynatrace_extension/sdk/vendor/mureq/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
29
30
|
dynatrace_extension/sdk/vendor/mureq/mureq.py,sha256=znF4mvzk5L03CLNozRz8UpK-fMijmSkObDFwlbhwLUg,14656
|
30
|
-
dt_extensions_sdk-1.
|
31
|
-
dt_extensions_sdk-1.
|
32
|
-
dt_extensions_sdk-1.
|
33
|
-
dt_extensions_sdk-1.
|
34
|
-
dt_extensions_sdk-1.
|
31
|
+
dt_extensions_sdk-1.3.1.dist-info/METADATA,sha256=LwKOdwizaSteiT3s-yPkAXYRyqkiqQC11TaPXiGlJ-Q,2685
|
32
|
+
dt_extensions_sdk-1.3.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
33
|
+
dt_extensions_sdk-1.3.1.dist-info/entry_points.txt,sha256=pweyOCgENGHjOlT6_kXYaBPOrE3p18K0UettqnNlnoE,55
|
34
|
+
dt_extensions_sdk-1.3.1.dist-info/licenses/LICENSE.txt,sha256=3Zihv0lOVYHNfDkJC-tUAU6euP9r2NexsDW4w-zqgVk,1078
|
35
|
+
dt_extensions_sdk-1.3.1.dist-info/RECORD,,
|
dynatrace_extension/__about__.py
CHANGED
dynatrace_extension/cli/main.py
CHANGED
@@ -40,6 +40,7 @@ def version():
|
|
40
40
|
def run(
|
41
41
|
extension_dir: Path = typer.Argument("."),
|
42
42
|
activation_config: str = "activation.json",
|
43
|
+
secrets: str = "secrets.json",
|
43
44
|
fast_check: bool = typer.Option(False, "--fastcheck"),
|
44
45
|
local_ingest: bool = typer.Option(False, "--local-ingest"),
|
45
46
|
local_ingest_port: int = typer.Option(14499, "--local-ingest-port"),
|
@@ -50,6 +51,7 @@ def run(
|
|
50
51
|
|
51
52
|
:param extension_dir: The directory of the extension, by default this is the current directory
|
52
53
|
:param activation_config: The activation config file, by default this is activation.json
|
54
|
+
:param secrets: The secrets file to be used to enrich the activation config, by default this is secrets.json
|
53
55
|
:param fast_check: If true, run a fastcheck and exits
|
54
56
|
:param local_ingest: If true, send metrics to localhost:14499 on top of printing them
|
55
57
|
:param local_ingest_port: The port to send metrics to, by default this is 14499
|
@@ -59,7 +61,15 @@ def run(
|
|
59
61
|
# This parses the yaml, which validates it before running
|
60
62
|
extension_yaml = ExtensionYaml(extension_dir / "extension/extension.yaml")
|
61
63
|
try:
|
62
|
-
command = [
|
64
|
+
command = [
|
65
|
+
sys.executable,
|
66
|
+
"-m",
|
67
|
+
extension_yaml.python.runtime.module,
|
68
|
+
"--activationconfig",
|
69
|
+
activation_config,
|
70
|
+
"--secrets",
|
71
|
+
secrets,
|
72
|
+
]
|
63
73
|
if fast_check:
|
64
74
|
command.append("--fastcheck")
|
65
75
|
if local_ingest:
|
@@ -9,7 +9,7 @@ from timeit import default_timer as timer
|
|
9
9
|
from typing import Callable, Dict, Optional, Tuple
|
10
10
|
|
11
11
|
from .activation import ActivationType
|
12
|
-
from .communication import Status, StatusValue
|
12
|
+
from .communication import MultiStatus, Status, StatusValue
|
13
13
|
|
14
14
|
|
15
15
|
class WrappedCallback:
|
@@ -60,8 +60,13 @@ class WrappedCallback:
|
|
60
60
|
start_time = timer()
|
61
61
|
failed = False
|
62
62
|
try:
|
63
|
-
self.callback(*self.callback_args, **self.callback_kwargs)
|
64
|
-
|
63
|
+
ret = self.callback(*self.callback_args, **self.callback_kwargs)
|
64
|
+
if isinstance(ret, Status):
|
65
|
+
self.status = ret
|
66
|
+
elif isinstance(ret, MultiStatus):
|
67
|
+
self.status = ret.build()
|
68
|
+
else:
|
69
|
+
self.status = Status(StatusValue.OK)
|
65
70
|
except Exception as e:
|
66
71
|
failed = True
|
67
72
|
self.logger.exception(f"Error running callback {self}: {e!r}")
|
@@ -59,6 +59,29 @@ class Status:
|
|
59
59
|
return self.status not in (StatusValue.OK, StatusValue.EMPTY)
|
60
60
|
|
61
61
|
|
62
|
+
class MultiStatus:
|
63
|
+
|
64
|
+
def __init__(self):
|
65
|
+
self.statuses = []
|
66
|
+
|
67
|
+
def add_status(self, status: StatusValue, message):
|
68
|
+
self.statuses.append(Status(status, message))
|
69
|
+
|
70
|
+
def build(self) -> Status:
|
71
|
+
ret = Status(StatusValue.OK)
|
72
|
+
if len(self.statuses) == 0:
|
73
|
+
return ret
|
74
|
+
|
75
|
+
messages = []
|
76
|
+
for stored_status in self.statuses:
|
77
|
+
print(stored_status) # noqa: T201
|
78
|
+
if stored_status.is_error():
|
79
|
+
ret.status = stored_status.status
|
80
|
+
messages.append(stored_status.message)
|
81
|
+
ret.message = "\n".join(messages)
|
82
|
+
return ret
|
83
|
+
|
84
|
+
|
62
85
|
class CommunicationClient(ABC):
|
63
86
|
"""
|
64
87
|
Abstract class for extension communication
|
@@ -324,14 +347,24 @@ class DebugClient(CommunicationClient):
|
|
324
347
|
activation_config_path: str,
|
325
348
|
extension_config_path: str,
|
326
349
|
logger: logging.Logger,
|
350
|
+
secrets_path: str = "secrets.json",
|
327
351
|
local_ingest: bool = False,
|
328
352
|
local_ingest_port: int = 14499,
|
329
353
|
print_metrics: bool = True,
|
330
354
|
):
|
355
|
+
|
356
|
+
self.secrets = {}
|
357
|
+
if secrets_path and Path(secrets_path).exists():
|
358
|
+
with open(secrets_path) as f:
|
359
|
+
self.secrets = json.load(f)
|
360
|
+
|
331
361
|
self.activation_config = {}
|
332
362
|
if activation_config_path and Path(activation_config_path).exists():
|
333
363
|
with open(activation_config_path) as f:
|
334
|
-
|
364
|
+
raw_activation_config = f.read()
|
365
|
+
self.activation_config = json.loads(
|
366
|
+
self.replace_secrets_in_activation_config(self.secrets, raw_activation_config)
|
367
|
+
)
|
335
368
|
|
336
369
|
self.extension_config = ""
|
337
370
|
if not extension_config_path:
|
@@ -430,6 +463,12 @@ class DebugClient(CommunicationClient):
|
|
430
463
|
def get_cluster_time_diff(self) -> int:
|
431
464
|
return 0
|
432
465
|
|
466
|
+
def replace_secrets_in_activation_config(self, secrets: dict, activation_config_string: str) -> str:
|
467
|
+
for secret_name, secret_value in secrets.items():
|
468
|
+
activation_config_string = activation_config_string.replace(f"{{{{{secret_name}}}}}", str(secret_value))
|
469
|
+
|
470
|
+
return activation_config_string
|
471
|
+
|
433
472
|
|
434
473
|
def divide_into_batches(
|
435
474
|
items: Sequence[dict | str], max_size_bytes: int, join_with: str | None = None
|
@@ -32,6 +32,7 @@ TIME_DIFF_INTERVAL = timedelta(seconds=60)
|
|
32
32
|
|
33
33
|
CALLBACKS_THREAD_POOL_SIZE = 100
|
34
34
|
INTERNAL_THREAD_POOL_SIZE = 20
|
35
|
+
HEARTBEAT_THREAD_POOL_SIZE = 10
|
35
36
|
|
36
37
|
RFC_3339_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
|
37
38
|
DATASOURCE_TYPE = "python"
|
@@ -220,6 +221,7 @@ class Extension:
|
|
220
221
|
# Executors for the callbacks and internal methods
|
221
222
|
self._callbacks_executor = ThreadPoolExecutor(max_workers=CALLBACKS_THREAD_POOL_SIZE)
|
222
223
|
self._internal_executor = ThreadPoolExecutor(max_workers=INTERNAL_THREAD_POOL_SIZE)
|
224
|
+
self._heartbeat_executor = ThreadPoolExecutor(max_workers=HEARTBEAT_THREAD_POOL_SIZE)
|
223
225
|
|
224
226
|
# Extension metrics
|
225
227
|
self._metrics_lock = RLock()
|
@@ -715,6 +717,7 @@ class Extension:
|
|
715
717
|
# Debug parameters, these are used when running the extension locally
|
716
718
|
parser.add_argument("--extensionconfig", required=False, default=None)
|
717
719
|
parser.add_argument("--activationconfig", required=False, default="activation.json")
|
720
|
+
parser.add_argument("--secrets", required=False, default="secrets.json")
|
718
721
|
parser.add_argument("--no-print-metrics", required=False, action="store_true")
|
719
722
|
|
720
723
|
args, unknown = parser.parse_known_args()
|
@@ -727,6 +730,7 @@ class Extension:
|
|
727
730
|
activation_config_path=args.activationconfig,
|
728
731
|
extension_config_path=args.extensionconfig,
|
729
732
|
logger=api_logger,
|
733
|
+
secrets_path=args.secrets,
|
730
734
|
local_ingest=args.local_ingest,
|
731
735
|
local_ingest_port=args.local_ingest_port,
|
732
736
|
print_metrics=print_metrics,
|
@@ -825,7 +829,7 @@ class Extension:
|
|
825
829
|
self._scheduler.enterabs(next_timestamp, 1, self._timediff_iteration)
|
826
830
|
|
827
831
|
def _heartbeat_iteration(self):
|
828
|
-
self.
|
832
|
+
self._heartbeat_executor.submit(self._heartbeat)
|
829
833
|
next_timestamp = self._get_and_set_next_internal_callback_timestamp("heartbeat", HEARTBEAT_INTERVAL)
|
830
834
|
self._scheduler.enterabs(next_timestamp, 2, self._heartbeat_iteration)
|
831
835
|
|
@@ -974,7 +978,9 @@ class Extension:
|
|
974
978
|
if callback.status.is_error():
|
975
979
|
overall_status.status = callback.status.status
|
976
980
|
messages.append(f"{callback}: {callback.status.message}")
|
977
|
-
|
981
|
+
continue
|
982
|
+
if callback.status.message is not None and callback.status.message != "":
|
983
|
+
messages.append(f"{callback}: {callback.status.message}")
|
978
984
|
overall_status.message = "\n".join(messages)
|
979
985
|
return overall_status
|
980
986
|
|
File without changes
|
{dt_extensions_sdk-1.2.14.dist-info → dt_extensions_sdk-1.3.1.dist-info}/licenses/LICENSE.txt
RENAMED
File without changes
|