omnata-plugin-runtime 0.8.0a188__tar.gz → 0.8.0a190__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {omnata_plugin_runtime-0.8.0a188 → omnata_plugin_runtime-0.8.0a190}/PKG-INFO +1 -1
- {omnata_plugin_runtime-0.8.0a188 → omnata_plugin_runtime-0.8.0a190}/pyproject.toml +1 -1
- {omnata_plugin_runtime-0.8.0a188 → omnata_plugin_runtime-0.8.0a190}/src/omnata_plugin_runtime/configuration.py +3 -1
- {omnata_plugin_runtime-0.8.0a188 → omnata_plugin_runtime-0.8.0a190}/src/omnata_plugin_runtime/plugin_entrypoints.py +49 -40
- {omnata_plugin_runtime-0.8.0a188 → omnata_plugin_runtime-0.8.0a190}/src/omnata_plugin_runtime/rate_limiting.py +15 -9
- {omnata_plugin_runtime-0.8.0a188 → omnata_plugin_runtime-0.8.0a190}/LICENSE +0 -0
- {omnata_plugin_runtime-0.8.0a188 → omnata_plugin_runtime-0.8.0a190}/README.md +0 -0
- {omnata_plugin_runtime-0.8.0a188 → omnata_plugin_runtime-0.8.0a190}/src/omnata_plugin_runtime/__init__.py +0 -0
- {omnata_plugin_runtime-0.8.0a188 → omnata_plugin_runtime-0.8.0a190}/src/omnata_plugin_runtime/api.py +0 -0
- {omnata_plugin_runtime-0.8.0a188 → omnata_plugin_runtime-0.8.0a190}/src/omnata_plugin_runtime/forms.py +0 -0
- {omnata_plugin_runtime-0.8.0a188 → omnata_plugin_runtime-0.8.0a190}/src/omnata_plugin_runtime/logging.py +0 -0
- {omnata_plugin_runtime-0.8.0a188 → omnata_plugin_runtime-0.8.0a190}/src/omnata_plugin_runtime/omnata_plugin.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "omnata-plugin-runtime"
|
3
|
-
version = "0.8.0-
|
3
|
+
version = "0.8.0-a190"
|
4
4
|
description = "Classes and common runtime components for building and running Omnata Plugins"
|
5
5
|
authors = ["James Weakley <james.weakley@omnata.com>"]
|
6
6
|
readme = "README.md"
|
@@ -19,7 +19,9 @@ if tuple(sys.version_info[:2]) >= (3, 9):
|
|
19
19
|
else:
|
20
20
|
# Python 3.8 and below
|
21
21
|
from typing_extensions import Annotated
|
22
|
+
from opentelemetry import trace
|
22
23
|
|
24
|
+
tracer = trace.get_tracer(__name__)
|
23
25
|
|
24
26
|
class MapperType(str, Enum):
|
25
27
|
FIELD_MAPPING_SELECTOR = "field_mapping_selector"
|
@@ -871,7 +873,7 @@ InboundSyncStreamsConfiguration.model_rebuild()
|
|
871
873
|
StoredFieldMappings.model_rebuild()
|
872
874
|
OutboundSyncConfigurationParameters.model_rebuild()
|
873
875
|
|
874
|
-
|
876
|
+
@tracer.start_as_current_span("get_secrets")
|
875
877
|
def get_secrets(oauth_secret_name: Optional[str], other_secrets_name: Optional[str]
|
876
878
|
) -> Dict[str, StoredConfigurationValue]:
|
877
879
|
connection_secrets = {}
|
@@ -39,7 +39,7 @@ from .rate_limiting import ApiLimits, RateLimitState
|
|
39
39
|
from opentelemetry import trace
|
40
40
|
|
41
41
|
IMPORT_DIRECTORY_NAME = "snowflake_import_directory"
|
42
|
-
|
42
|
+
tracer = trace.get_tracer(__name__)
|
43
43
|
|
44
44
|
class PluginEntrypoint:
|
45
45
|
"""
|
@@ -51,8 +51,7 @@ class PluginEntrypoint:
|
|
51
51
|
self, plugin_fqn: str, session: Session, module_name: str, class_name: str
|
52
52
|
):
|
53
53
|
logger.info(f"Initialising plugin entrypoint for {plugin_fqn}")
|
54
|
-
|
55
|
-
with self.tracer.start_as_current_span("plugin_initialization") as span:
|
54
|
+
with tracer.start_as_current_span("plugin_initialization") as span:
|
56
55
|
self._session = session
|
57
56
|
import_dir = sys._xoptions[IMPORT_DIRECTORY_NAME]
|
58
57
|
span.add_event("Adding plugin zip to path")
|
@@ -93,7 +92,7 @@ class PluginEntrypoint:
|
|
93
92
|
logger.add_extra('omnata.sync_branch.id', request.sync_branch_id)
|
94
93
|
logger.add_extra('omnata.sync_branch.name', request.sync_branch_name)
|
95
94
|
logger.info("Entered sync method")
|
96
|
-
with
|
95
|
+
with tracer.start_as_current_span("initialization") as span:
|
97
96
|
span.add_event("Fetching secrets")
|
98
97
|
|
99
98
|
request = TypeAdapter(SyncRequestPayload).validate_python(sync_request)
|
@@ -186,11 +185,11 @@ class PluginEntrypoint:
|
|
186
185
|
)
|
187
186
|
try:
|
188
187
|
self._plugin_instance._configuration_parameters = parameters
|
189
|
-
with
|
188
|
+
with tracer.start_as_current_span("invoke_plugin") as span:
|
190
189
|
with HttpRateLimiting(outbound_sync_request, parameters):
|
191
190
|
self._plugin_instance.sync_outbound(parameters, outbound_sync_request)
|
192
191
|
if self._plugin_instance.disable_background_workers is False:
|
193
|
-
with
|
192
|
+
with tracer.start_as_current_span("results_finalization") as span:
|
194
193
|
outbound_sync_request.apply_results_queue()
|
195
194
|
outbound_sync_request.apply_rate_limit_state()
|
196
195
|
if outbound_sync_request.deadline_reached:
|
@@ -246,12 +245,12 @@ class PluginEntrypoint:
|
|
246
245
|
inbound_sync_request.update_activity("Invoking plugin")
|
247
246
|
logger.info(f"inbound sync request: {inbound_sync_request}")
|
248
247
|
# plugin_instance._inbound_sync_request = outbound_sync_request
|
249
|
-
with
|
248
|
+
with tracer.start_as_current_span("invoke_plugin"):
|
250
249
|
with HttpRateLimiting(inbound_sync_request, parameters):
|
251
250
|
self._plugin_instance.sync_inbound(parameters, inbound_sync_request)
|
252
251
|
logger.info("Finished invoking plugin")
|
253
252
|
if self._plugin_instance.disable_background_workers is False:
|
254
|
-
with
|
253
|
+
with tracer.start_as_current_span("results_finalization") as span:
|
255
254
|
inbound_sync_request.update_activity("Staging remaining records")
|
256
255
|
logger.info("Calling apply_results_queue")
|
257
256
|
inbound_sync_request.apply_results_queue()
|
@@ -304,6 +303,8 @@ class PluginEntrypoint:
|
|
304
303
|
sync_parameters: Dict,
|
305
304
|
current_form_parameters: Optional[Dict],
|
306
305
|
):
|
306
|
+
if function_name is None:
|
307
|
+
function_name = f"{sync_direction}_configuration_form"
|
307
308
|
logger.add_extra('omnata.operation', 'configuration_form')
|
308
309
|
logger.add_extra('omnata.connection.connectivity_option', connectivity_option)
|
309
310
|
logger.add_extra('omnata.connection.connection_method', connection_method)
|
@@ -349,9 +350,10 @@ class PluginEntrypoint:
|
|
349
350
|
parameters.access_token_secret_name = oauth_secret_name
|
350
351
|
the_function = getattr(
|
351
352
|
self._plugin_instance,
|
352
|
-
function_name
|
353
|
+
function_name
|
353
354
|
)
|
354
|
-
|
355
|
+
with tracer.start_as_current_span("invoke_plugin"):
|
356
|
+
script_result = the_function(parameters)
|
355
357
|
if isinstance(script_result, BaseModel):
|
356
358
|
script_result = script_result.model_dump()
|
357
359
|
elif isinstance(script_result, List):
|
@@ -393,8 +395,8 @@ class PluginEntrypoint:
|
|
393
395
|
)
|
394
396
|
if oauth_secret_name is not None:
|
395
397
|
parameters.access_token_secret_name = oauth_secret_name
|
396
|
-
|
397
|
-
|
398
|
+
with tracer.start_as_current_span("invoke_plugin"):
|
399
|
+
script_result = self._plugin_instance.inbound_stream_list(parameters)
|
398
400
|
if isinstance(script_result, BaseModel):
|
399
401
|
script_result = script_result.model_dump()
|
400
402
|
elif isinstance(script_result, List):
|
@@ -428,19 +430,21 @@ class PluginEntrypoint:
|
|
428
430
|
logger.add_extra('omnata.connection.connectivity_option', connectivity_option)
|
429
431
|
connectivity_option = TypeAdapter(ConnectivityOption).validate_python(connectivity_option)
|
430
432
|
logger.info("Entered connection_form method")
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
433
|
+
with tracer.start_as_current_span("invoke_plugin"):
|
434
|
+
if self._plugin_instance.connection_form.__code__.co_argcount==1:
|
435
|
+
form: List[ConnectionMethod] = self._plugin_instance.connection_form()
|
436
|
+
else:
|
437
|
+
form: List[ConnectionMethod] = self._plugin_instance.connection_form(connectivity_option)
|
435
438
|
return [f.model_dump() for f in form]
|
436
439
|
|
437
440
|
def create_billing_events(self, session, event_request: Dict):
|
438
441
|
logger.add_extra('omnata.operation', 'create_billing_events')
|
439
442
|
logger.info("Entered create_billing_events method")
|
440
443
|
request = TypeAdapter(BillingEventRequest).validate_python(event_request)
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
+
with tracer.start_as_current_span("invoke_plugin"):
|
445
|
+
events: List[SnowflakeBillingEvent] = self._plugin_instance.create_billing_events(
|
446
|
+
request
|
447
|
+
)
|
444
448
|
# create each billing event, waiting a second between each one
|
445
449
|
first_time = True
|
446
450
|
for billing_event in events:
|
@@ -527,31 +531,35 @@ class PluginEntrypoint:
|
|
527
531
|
)
|
528
532
|
if oauth_secret_name is not None:
|
529
533
|
parameters.access_token_secret_name = oauth_secret_name
|
530
|
-
|
531
|
-
|
532
|
-
|
534
|
+
with tracer.start_as_current_span("invoke_plugin"):
|
535
|
+
connect_response = self._plugin_instance.connect(
|
536
|
+
parameters=parameters
|
537
|
+
)
|
533
538
|
# the connect method can also return more network addresses. If so, we need to update the
|
534
539
|
# network rule associated with the external access integration
|
535
540
|
if connect_response is None:
|
536
541
|
raise ValueError("Plugin did not return a ConnectResponse object from the connect method")
|
537
542
|
if connect_response.network_addresses is not None:
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
|
552
|
-
|
553
|
-
f"
|
554
|
-
|
543
|
+
with tracer.start_as_current_span("network_rule_update") as network_rule_update_span:
|
544
|
+
network_rule_update_span.add_event("Retrieving existing network rule")
|
545
|
+
existing_rule_result = self._session.sql(
|
546
|
+
f"desc network rule {network_rule_name}"
|
547
|
+
).collect()
|
548
|
+
rule_values: List[str] = existing_rule_result[0].value_list.split(",")
|
549
|
+
rule_values = [r for r in rule_values if r != '']
|
550
|
+
logger.info(f"Existing rules for {network_rule_name}: {rule_values}")
|
551
|
+
for network_address in connect_response.network_addresses:
|
552
|
+
if network_address not in rule_values:
|
553
|
+
rule_values.append(network_address)
|
554
|
+
#if len(rule_values)==0:
|
555
|
+
# logger.info("No network addresses for plugin, adding localhost")
|
556
|
+
# rule_values.append("https://localhost")
|
557
|
+
logger.info(f"New rules for {network_rule_name}: {rule_values}")
|
558
|
+
rule_values_string = ",".join([f"'{value}'" for value in rule_values])
|
559
|
+
network_rule_update_span.add_event("Updating network rule")
|
560
|
+
self._session.sql(
|
561
|
+
f"alter network rule {network_rule_name} set value_list = ({rule_values_string})"
|
562
|
+
).collect()
|
555
563
|
|
556
564
|
return connect_response.model_dump()
|
557
565
|
|
@@ -578,7 +586,8 @@ class PluginEntrypoint:
|
|
578
586
|
)
|
579
587
|
if oauth_secret_name is not None:
|
580
588
|
connection_parameters.access_token_secret_name = oauth_secret_name
|
581
|
-
|
589
|
+
with tracer.start_as_current_span("invoke_plugin"):
|
590
|
+
response: List[ApiLimits] = self._plugin_instance.api_limits(connection_parameters)
|
582
591
|
return [api_limit.model_dump() for api_limit in response]
|
583
592
|
|
584
593
|
def outbound_record_validator(
|
@@ -19,6 +19,9 @@ from .logging import logger
|
|
19
19
|
import pytz
|
20
20
|
from requests.adapters import HTTPAdapter
|
21
21
|
from urllib3.util.retry import Retry
|
22
|
+
from opentelemetry import trace
|
23
|
+
|
24
|
+
tracer = trace.get_tracer(__name__)
|
22
25
|
|
23
26
|
TimeUnitType = Literal["second", "minute", "hour", "day"]
|
24
27
|
|
@@ -383,11 +386,12 @@ class RetryWithLogging(Retry):
|
|
383
386
|
retry_after = self.get_retry_after(response)
|
384
387
|
if retry_after:
|
385
388
|
logger.info(f"Retrying after {retry_after} seconds due to Retry-After header")
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
389
|
+
with tracer.start_as_current_span("http_retry_wait"):
|
390
|
+
if self.thread_cancellation_token is None:
|
391
|
+
time.sleep(retry_after)
|
392
|
+
else:
|
393
|
+
if self.thread_cancellation_token.wait(retry_after):
|
394
|
+
raise InterruptedWhileWaitingException(message="The sync was interrupted while waiting for rate limiting to expire")
|
391
395
|
return True
|
392
396
|
return False
|
393
397
|
|
@@ -504,8 +508,9 @@ class RateLimitedSession(requests.Session):
|
|
504
508
|
raise InterruptedWhileWaitingException(message=f"The rate limiting wait time ({wait_time} seconds) would exceed the run deadline")
|
505
509
|
logger.info(f"Waiting for {wait_time} seconds before retrying {method} request to {url}")
|
506
510
|
# if wait() returns true, it means that the thread was cancelled
|
507
|
-
|
508
|
-
|
511
|
+
with tracer.start_as_current_span("http_retry_wait"):
|
512
|
+
if self.thread_cancellation_token.wait(wait_time):
|
513
|
+
raise InterruptedWhileWaitingException(message="The sync was interrupted while waiting for rate limiting to expire")
|
509
514
|
else:
|
510
515
|
current_url_retries = self.increment_retries(url)
|
511
516
|
if current_url_retries >= self.max_retries:
|
@@ -514,8 +519,9 @@ class RateLimitedSession(requests.Session):
|
|
514
519
|
if datetime.datetime.now(pytz.UTC) + datetime.timedelta(seconds=backoff_time) > self.run_deadline:
|
515
520
|
raise InterruptedWhileWaitingException(message=f"The rate limiting backoff time ({backoff_time} seconds) would exceed the run deadline")
|
516
521
|
logger.info(f"Waiting for {backoff_time} seconds before retrying {method} request to {url}")
|
517
|
-
|
518
|
-
|
522
|
+
with tracer.start_as_current_span("http_retry_wait"):
|
523
|
+
if self.thread_cancellation_token.wait(backoff_time):
|
524
|
+
raise InterruptedWhileWaitingException(message="The sync was interrupted while waiting for rate limiting backoff")
|
519
525
|
else:
|
520
526
|
self.set_retries(url,0) # Reset retries if the request is successful
|
521
527
|
return response
|
File without changes
|
File without changes
|
File without changes
|
{omnata_plugin_runtime-0.8.0a188 → omnata_plugin_runtime-0.8.0a190}/src/omnata_plugin_runtime/api.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|