insightconnect-plugin-runtime 6.2.5__tar.gz → 6.3.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.
Files changed (92) hide show
  1. {insightconnect_plugin_runtime-6.2.5/insightconnect_plugin_runtime.egg-info → insightconnect_plugin_runtime-6.3.0}/PKG-INFO +9 -2
  2. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/README.md +2 -0
  3. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/api/endpoints.py +6 -6
  4. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/server.py +29 -19
  5. insightconnect_plugin_runtime-6.3.0/insightconnect_plugin_runtime/telemetry.py +73 -0
  6. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/util.py +1 -0
  7. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0/insightconnect_plugin_runtime.egg-info}/PKG-INFO +9 -2
  8. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime.egg-info/SOURCES.txt +1 -0
  9. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime.egg-info/requires.txt +5 -0
  10. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/setup.py +7 -2
  11. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/unit/test_server_cloud_plugins.py +9 -4
  12. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/MANIFEST.in +0 -0
  13. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect-plugin-swagger.json +0 -0
  14. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/__init__.py +0 -0
  15. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/action.py +0 -0
  16. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/api/__init__.py +0 -0
  17. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/api/schemas.py +0 -0
  18. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/cli.py +0 -0
  19. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/clients/__init__.py +0 -0
  20. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/clients/aws_client.py +0 -0
  21. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/clients/oauth.py +0 -0
  22. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/connection.py +0 -0
  23. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/data/input_message_schema.json +0 -0
  24. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/data/output_message_schema.json +0 -0
  25. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/dispatcher.py +0 -0
  26. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/exceptions.py +0 -0
  27. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/helper.py +0 -0
  28. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/metrics.py +0 -0
  29. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/plugin.py +0 -0
  30. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/schema.py +0 -0
  31. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/step.py +0 -0
  32. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/task.py +0 -0
  33. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/trigger.py +0 -0
  34. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime/variables.py +0 -0
  35. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime.egg-info/dependency_links.txt +0 -0
  36. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/insightconnect_plugin_runtime.egg-info/top_level.txt +0 -0
  37. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/setup.cfg +0 -0
  38. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/__init__.py +0 -0
  39. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/__init__.py +0 -0
  40. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/__init__.py +0 -0
  41. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/__init__.py +0 -0
  42. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/__init__.py +0 -0
  43. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/actions/__init__.py +0 -0
  44. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/actions/hello/__init__.py +0 -0
  45. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/actions/hello/action.py +0 -0
  46. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/actions/hello/schema.py +0 -0
  47. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/actions/return_bad_json/__init__.py +0 -0
  48. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/actions/return_bad_json/action.py +0 -0
  49. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/actions/return_bad_json/schema.py +0 -0
  50. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/actions/throw_exception/__init__.py +0 -0
  51. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/actions/throw_exception/action.py +0 -0
  52. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/actions/throw_exception/schema.py +0 -0
  53. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/connection/__init__.py +0 -0
  54. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/connection/connection.py +0 -0
  55. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/connection/schema.py +0 -0
  56. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/tasks/__init__.py +0 -0
  57. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/tasks/monitor_events/__init__.py +0 -0
  58. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/tasks/monitor_events/schema.py +0 -0
  59. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/tasks/monitor_events/task.py +0 -0
  60. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/triggers/__init__.py +0 -0
  61. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/triggers/hello_trigger/__init__.py +0 -0
  62. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/triggers/hello_trigger/schema.py +0 -0
  63. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/triggers/hello_trigger/trigger.py +0 -0
  64. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/triggers/return_bad_json_trigger/__init__.py +0 -0
  65. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/triggers/return_bad_json_trigger/schema.py +0 -0
  66. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/triggers/return_bad_json_trigger/trigger.py +0 -0
  67. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/triggers/throw_exception_trigger/__init__.py +0 -0
  68. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/triggers/throw_exception_trigger/schema.py +0 -0
  69. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/triggers/throw_exception_trigger/trigger.py +0 -0
  70. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/komand_hello_world/util/__init__.py +0 -0
  71. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/hello_world/setup.py +0 -0
  72. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/tests/__init__.py +0 -0
  73. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/tests/conftest.py +0 -0
  74. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/tests/test_cli.py +0 -0
  75. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/tests/test_hello_world.py +0 -0
  76. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/plugin/hello_world/tests/test_server.py +0 -0
  77. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/unit/__init__.py +0 -0
  78. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/unit/test_action.py +0 -0
  79. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/unit/test_api.py +0 -0
  80. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/unit/test_aws_action.py +0 -0
  81. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/unit/test_custom_encoder.py +0 -0
  82. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/unit/test_endpoints.py +0 -0
  83. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/unit/test_exceptions.py +0 -0
  84. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/unit/test_helpers.py +0 -0
  85. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/unit/test_metrics.py +0 -0
  86. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/unit/test_oauth.py +0 -0
  87. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/unit/test_plugin.py +0 -0
  88. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/unit/test_schema.py +0 -0
  89. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/unit/test_server_spec.py +0 -0
  90. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/unit/test_trigger.py +0 -0
  91. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/unit/test_variables.py +0 -0
  92. {insightconnect_plugin_runtime-6.2.5 → insightconnect_plugin_runtime-6.3.0}/tests/unit/utils.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: insightconnect-plugin-runtime
3
- Version: 6.2.5
3
+ Version: 6.3.0
4
4
  Summary: InsightConnect Plugin Runtime
5
5
  Home-page: https://github.com/rapid7/komand-plugin-sdk-python
6
6
  Author: Rapid7 Integrations Alliance
@@ -26,6 +26,11 @@ Requires-Dist: apispec-webframeworks==1.0.0
26
26
  Requires-Dist: blinker==1.9.0
27
27
  Requires-Dist: structlog==24.4.0
28
28
  Requires-Dist: python-json-logger==2.0.7
29
+ Requires-Dist: Jinja2==3.1.6
30
+ Requires-Dist: opentelemetry-sdk==1.31.1
31
+ Requires-Dist: opentelemetry-instrumentation-flask==0.52b1
32
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http==1.31.1
33
+ Requires-Dist: opentelemetry-instrumentation-requests==0.52b1
29
34
  Dynamic: author
30
35
  Dynamic: author-email
31
36
  Dynamic: classifier
@@ -219,6 +224,8 @@ contributed. Black is installed as a test dependency and the hook can be initial
219
224
  after cloning this repository.
220
225
 
221
226
  ## Changelog
227
+ * 6.3.0 - Add Tracing Instrumentation
228
+ * 6.2.6 - Remove setuptools after installation
222
229
  * 6.2.5 - Fixed bug related to failure to set default region to assume role method in `aws_client` for newer versions of boto3 | Updated alpine image packages on build
223
230
  * 6.2.4 - Update `make_request` helper to support extra parameter of `max_response_size` to cap the response
224
231
  * 6.2.3 - Updated dockerfiles for both `slim` and `full` SDK types to use Python 3.11.11 | Updated dependencies | Removed `pkg_resources` usage due to deprecation
@@ -182,6 +182,8 @@ contributed. Black is installed as a test dependency and the hook can be initial
182
182
  after cloning this repository.
183
183
 
184
184
  ## Changelog
185
+ * 6.3.0 - Add Tracing Instrumentation
186
+ * 6.2.6 - Remove setuptools after installation
185
187
  * 6.2.5 - Fixed bug related to failure to set default region to assume role method in `aws_client` for newer versions of boto3 | Updated alpine image packages on build
186
188
  * 6.2.4 - Update `make_request` helper to support extra parameter of `max_response_size` to cap the response
187
189
  * 6.2.3 - Updated dockerfiles for both `slim` and `full` SDK types to use Python 3.11.11 | Updated dependencies | Removed `pkg_resources` usage due to deprecation
@@ -787,7 +787,7 @@ class Endpoints:
787
787
  return version
788
788
 
789
789
  def add_plugin_custom_config(
790
- self, input_data: Dict[str, Any], org_id: str
790
+ self, input_data: Dict[str, Any], org_id: str
791
791
  ) -> Dict[str, Any]:
792
792
  """
793
793
  Using the retrieved configs pulled from komand-props, pass the configuration that matches the requesting
@@ -815,7 +815,7 @@ class Endpoints:
815
815
  # This means we still need to manually delete the state for plugins on a per org basis.
816
816
  # This also means first time customers for their 'initial' lookup would get the lookback value passed in.
817
817
  if input_data.get("body", {}).get("state") and additional_config.get(
818
- "lookback"
818
+ "lookback"
819
819
  ):
820
820
  self.logger.info(
821
821
  "Found an existing plugin state, not passing lookback value..."
@@ -842,8 +842,8 @@ class Endpoints:
842
842
  if isinstance(wrapped_exception, ClientException):
843
843
  status_code = 400
844
844
  elif (
845
- isinstance(wrapped_exception, PluginException)
846
- and wrapped_exception.preset is PluginException.Preset.BAD_REQUEST
845
+ isinstance(wrapped_exception, PluginException)
846
+ and wrapped_exception.preset is PluginException.Preset.BAD_REQUEST
847
847
  ):
848
848
  status_code = 400
849
849
  elif isinstance(wrapped_exception, (ConnectionTestException, ClientException)):
@@ -868,8 +868,8 @@ class Endpoints:
868
868
  if isinstance(wrapped_exception, (ConnectionTestException, ClientException)):
869
869
  return 400
870
870
  elif (
871
- isinstance(wrapped_exception, PluginException)
872
- and wrapped_exception.preset is PluginException.Preset.BAD_REQUEST
871
+ isinstance(wrapped_exception, PluginException)
872
+ and wrapped_exception.preset is PluginException.Preset.BAD_REQUEST
873
873
  ):
874
874
  return 400
875
875
  elif isinstance(wrapped_exception, ServerException):
@@ -1,17 +1,16 @@
1
- import os
2
- import sys
3
1
  import json
4
2
  import logging
3
+ import os
4
+ import sys
5
+ from time import sleep
5
6
 
7
+ import gunicorn.app.base
8
+ import structlog
6
9
  from apispec import APISpec
7
10
  from apispec.ext.marshmallow import MarshmallowPlugin
8
11
  from apispec_webframeworks.flask import FlaskPlugin
9
-
10
12
  from flask import Flask, request_started, request
11
- import gunicorn.app.base
12
13
  from gunicorn.arbiter import Arbiter
13
-
14
- import structlog
15
14
  from pythonjsonlogger.jsonlogger import JsonFormatter
16
15
  from requests import get as request_get
17
16
  from requests.exceptions import (
@@ -21,9 +20,9 @@ from requests.exceptions import (
21
20
  JSONDecodeError,
22
21
  ConnectionError,
23
22
  )
24
- from time import sleep
25
23
  from werkzeug.utils import secure_filename
26
24
 
25
+ from insightconnect_plugin_runtime.api.endpoints import Endpoints, handle_errors
27
26
  from insightconnect_plugin_runtime.api.schemas import (
28
27
  PluginInfoSchema,
29
28
  ActionTriggerOutputBodySchema,
@@ -39,9 +38,9 @@ from insightconnect_plugin_runtime.api.schemas import (
39
38
  ConnectionDetailsSchema,
40
39
  ConnectionTestSchema,
41
40
  )
42
- from insightconnect_plugin_runtime.api.endpoints import Endpoints, handle_errors
43
- from insightconnect_plugin_runtime.util import is_running_in_cloud
44
41
  from insightconnect_plugin_runtime.helper import clean_dict
42
+ from insightconnect_plugin_runtime.telemetry import create_post_fork
43
+ from insightconnect_plugin_runtime.util import is_running_in_cloud, OTEL_ENDPOINT
45
44
 
46
45
  API_TITLE = "InsightConnect Plugin Runtime API"
47
46
  API_VERSION = "1.0"
@@ -72,14 +71,14 @@ class PluginServer(gunicorn.app.base.BaseApplication):
72
71
  """
73
72
 
74
73
  def __init__(
75
- self,
76
- plugin,
77
- port=10001,
78
- workers=1,
79
- threads=4,
80
- debug=False,
81
- worker_class="sync",
82
- worker_connections=200,
74
+ self,
75
+ plugin,
76
+ port=10001,
77
+ workers=1,
78
+ threads=4,
79
+ debug=False,
80
+ worker_class="sync",
81
+ worker_connections=200,
83
82
  ):
84
83
 
85
84
  gunicorn_file = os.environ.get("GUNICORN_CONFIG_FILE")
@@ -120,6 +119,9 @@ class PluginServer(gunicorn.app.base.BaseApplication):
120
119
  self.get_plugin_properties_from_cps()
121
120
  self.app, self.blueprints = self.create_flask_app()
122
121
 
122
+ if os.environ.get(OTEL_ENDPOINT):
123
+ self.config_options[OTEL_ENDPOINT] = os.environ[OTEL_ENDPOINT]
124
+
123
125
  @staticmethod
124
126
  def configure_structlog_instance(is_debug: bool) -> None:
125
127
  structlog.configure(
@@ -166,6 +168,9 @@ class PluginServer(gunicorn.app.base.BaseApplication):
166
168
  for key, value in config.items():
167
169
  self.cfg.set(key.lower(), value)
168
170
 
171
+ post_fork = create_post_fork(lambda: self.app, lambda: self.plugin, lambda: self.config_options)
172
+ self.cfg.set("post_fork", post_fork)
173
+
169
174
  def create_flask_app(self):
170
175
  app = Flask(__name__)
171
176
 
@@ -193,7 +198,7 @@ class PluginServer(gunicorn.app.base.BaseApplication):
193
198
 
194
199
  def get_plugin_properties_from_cps(self):
195
200
  # Call out to komand-props to get configurations related to only the plugin pod running.
196
- if is_running_in_cloud() and self.plugin.tasks:
201
+ if is_running_in_cloud():
197
202
  for attempt in range(1, CPS_RETRY + 1):
198
203
  self.logger.info(
199
204
  f"Getting plugin configuration information... (attempt {attempt}/{CPS_RETRY})"
@@ -206,7 +211,12 @@ class PluginServer(gunicorn.app.base.BaseApplication):
206
211
  ) # match how we name our images
207
212
  plugin_config = resp_json.get("plugins", {}).get(plugin, {})
208
213
 
209
- self.config_options = plugin_config
214
+ if self.plugin.tasks:
215
+ self.config_options = plugin_config
216
+
217
+ if resp_json.get(OTEL_ENDPOINT, {}):
218
+ self.config_options[OTEL_ENDPOINT] = resp_json.get(OTEL_ENDPOINT, {})
219
+
210
220
  self.logger.info("Plugin configuration successfully retrieved...")
211
221
  return
212
222
  except MissingSchema as missing_schema:
@@ -0,0 +1,73 @@
1
+ import functools
2
+ from typing import Any, Callable
3
+ from flask.app import Flask
4
+
5
+ from opentelemetry import trace
6
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
7
+ from opentelemetry.instrumentation.flask import FlaskInstrumentor
8
+ from opentelemetry.instrumentation.requests import RequestsInstrumentor
9
+ from opentelemetry.sdk.resources import Resource
10
+ from opentelemetry.sdk.trace import TracerProvider
11
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor
12
+ from opentelemetry.trace import Status, StatusCode
13
+
14
+ from insightconnect_plugin_runtime.plugin import Plugin
15
+ from insightconnect_plugin_runtime.util import is_running_in_cloud, OTEL_ENDPOINT
16
+
17
+
18
+ def init_tracing(app: Flask, plugin: Plugin, endpoint: str) -> None:
19
+ """
20
+ Initialize OpenTelemetry Tracing
21
+
22
+ The function sets up the tracer provider, span processor and exporter with auto-instrumentation
23
+
24
+ :param app: The Flask Application
25
+ :param plugin: The plugin to derive the service name from
26
+ :param endpoint: The Otel Endpoint to emit traces to
27
+ """
28
+
29
+ if not is_running_in_cloud():
30
+ return
31
+
32
+ resource = Resource(attributes={"service.name": f'{plugin.name.lower().replace(" ", "_")}-{plugin.version}'})
33
+
34
+ trace_provider = TracerProvider(resource=resource)
35
+ exporter = OTLPSpanExporter(endpoint=endpoint)
36
+ trace_provider.add_span_processor(BatchSpanProcessor(exporter))
37
+ trace.set_tracer_provider(trace_provider)
38
+
39
+ FlaskInstrumentor().instrument_app(app)
40
+
41
+ def requests_callback(span: trace.Span, _: Any, response: Any) -> None:
42
+ if hasattr(response, "status_code"):
43
+ span.set_status(Status(StatusCode.OK if response.status_code < 400 else StatusCode.ERROR))
44
+
45
+ RequestsInstrumentor().instrument(trace_provider=trace_provider, response_hook=requests_callback)
46
+
47
+
48
+ def auto_instrument(func: Callable) -> Callable:
49
+ """
50
+ Decorator that auto-instruments a function with a trace
51
+
52
+ :param func: function to instrument
53
+ :return:
54
+ """
55
+
56
+ @functools.wraps(func)
57
+ def wrapper(*args, **kwargs):
58
+ tracer = trace.get_tracer(__name__)
59
+ with tracer.start_as_current_span(func.__name__):
60
+ return func(*args, **kwargs)
61
+
62
+ return wrapper
63
+
64
+
65
+ def create_post_fork(app_getter: Callable, plugin_getter: Callable, config_getter: Callable) -> Callable:
66
+ def post_fork(server, worker):
67
+ app = app_getter()
68
+ plugin = plugin_getter()
69
+ endpoint = config_getter().get(OTEL_ENDPOINT, None)
70
+ if endpoint:
71
+ init_tracing(app, plugin, endpoint)
72
+
73
+ return post_fork
@@ -10,6 +10,7 @@ import python_jsonschema_objects as pjs
10
10
  KEYS_TO_CHECK_SECRETS = ("secretKey", "password", "key", "token")
11
11
  DEFAULT_REPLACE_THRESHOLD = 0.8
12
12
  DEFAULT_NUMBER_OF_ITERATIONS = 50
13
+ OTEL_ENDPOINT = "otel_tracing"
13
14
 
14
15
 
15
16
  class OutputMasker:
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: insightconnect-plugin-runtime
3
- Version: 6.2.5
3
+ Version: 6.3.0
4
4
  Summary: InsightConnect Plugin Runtime
5
5
  Home-page: https://github.com/rapid7/komand-plugin-sdk-python
6
6
  Author: Rapid7 Integrations Alliance
@@ -26,6 +26,11 @@ Requires-Dist: apispec-webframeworks==1.0.0
26
26
  Requires-Dist: blinker==1.9.0
27
27
  Requires-Dist: structlog==24.4.0
28
28
  Requires-Dist: python-json-logger==2.0.7
29
+ Requires-Dist: Jinja2==3.1.6
30
+ Requires-Dist: opentelemetry-sdk==1.31.1
31
+ Requires-Dist: opentelemetry-instrumentation-flask==0.52b1
32
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http==1.31.1
33
+ Requires-Dist: opentelemetry-instrumentation-requests==0.52b1
29
34
  Dynamic: author
30
35
  Dynamic: author-email
31
36
  Dynamic: classifier
@@ -219,6 +224,8 @@ contributed. Black is installed as a test dependency and the hook can be initial
219
224
  after cloning this repository.
220
225
 
221
226
  ## Changelog
227
+ * 6.3.0 - Add Tracing Instrumentation
228
+ * 6.2.6 - Remove setuptools after installation
222
229
  * 6.2.5 - Fixed bug related to failure to set default region to assume role method in `aws_client` for newer versions of boto3 | Updated alpine image packages on build
223
230
  * 6.2.4 - Update `make_request` helper to support extra parameter of `max_response_size` to cap the response
224
231
  * 6.2.3 - Updated dockerfiles for both `slim` and `full` SDK types to use Python 3.11.11 | Updated dependencies | Removed `pkg_resources` usage due to deprecation
@@ -15,6 +15,7 @@ insightconnect_plugin_runtime/schema.py
15
15
  insightconnect_plugin_runtime/server.py
16
16
  insightconnect_plugin_runtime/step.py
17
17
  insightconnect_plugin_runtime/task.py
18
+ insightconnect_plugin_runtime/telemetry.py
18
19
  insightconnect_plugin_runtime/trigger.py
19
20
  insightconnect_plugin_runtime/util.py
20
21
  insightconnect_plugin_runtime/variables.py
@@ -12,3 +12,8 @@ apispec-webframeworks==1.0.0
12
12
  blinker==1.9.0
13
13
  structlog==24.4.0
14
14
  python-json-logger==2.0.7
15
+ Jinja2==3.1.6
16
+ opentelemetry-sdk==1.31.1
17
+ opentelemetry-instrumentation-flask==0.52b1
18
+ opentelemetry-exporter-otlp-proto-http==1.31.1
19
+ opentelemetry-instrumentation-requests==0.52b1
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setup(
7
7
  name="insightconnect-plugin-runtime",
8
- version="6.2.5",
8
+ version="6.3.0",
9
9
  description="InsightConnect Plugin Runtime",
10
10
  long_description=long_description,
11
11
  long_description_content_type="text/markdown",
@@ -27,7 +27,12 @@ setup(
27
27
  "apispec-webframeworks==1.0.0",
28
28
  "blinker==1.9.0",
29
29
  "structlog==24.4.0",
30
- "python-json-logger==2.0.7"
30
+ "python-json-logger==2.0.7",
31
+ "Jinja2==3.1.6",
32
+ "opentelemetry-sdk==1.31.1",
33
+ "opentelemetry-instrumentation-flask==0.52b1",
34
+ "opentelemetry-exporter-otlp-proto-http==1.31.1",
35
+ "opentelemetry-instrumentation-requests==0.52b1",
31
36
  ],
32
37
  tests_require=[
33
38
  "pytest",
@@ -4,6 +4,7 @@ from unittest import TestCase, skip
4
4
  from unittest.mock import patch, MagicMock
5
5
 
6
6
  from insightconnect_plugin_runtime.server import PluginServer
7
+ from insightconnect_plugin_runtime.util import OTEL_ENDPOINT
7
8
  from tests.plugin.hello_world import KomandHelloWorld
8
9
  from .utils import MockResponse, Logger
9
10
 
@@ -20,15 +21,19 @@ class TestServerCloudPlugins(TestCase):
20
21
  @parameterized.expand([["Set cloud to false", False], ["Set cloud to true", True]])
21
22
  @patch("insightconnect_plugin_runtime.server.request_get")
22
23
  def test_cloud_plugin_no_tasks_ignore_cps(self, _test_name, cloud, mocked_req, mock_cloud, _run):
24
+ fake_endpoint = "http://fake.endpoint.com"
25
+ mocked_req.return_value = MockResponse({OTEL_ENDPOINT: fake_endpoint}) if cloud else MockResponse({})
23
26
  mock_cloud.return_value = cloud # Mock plugin running in cloud vs not
24
27
  self.plugin.tasks = None # ensure still no tasks as other tests edit this and could fail before reverting
25
- plugin_server = PluginServer(self.plugin) # this plugin has no tasks by default
26
28
 
29
+ plugin_server = PluginServer(self.plugin) # this plugin has no tasks by default
27
30
  plugin_server.start()
28
- self.assertEqual(plugin_server.config_options, {})
29
31
 
30
- # Plugin server never calls out to CPS as either we are not running in cloud mode or have no tasks.
31
- self.assertFalse(mocked_req.called)
32
+ self.assertEqual(plugin_server.config_options, {OTEL_ENDPOINT: fake_endpoint} if cloud else {})
33
+
34
+ # Plugin server calls out to CPS when cloud to get tracing endpoint
35
+ self.assertEqual(mocked_req.called, cloud)
36
+
32
37
 
33
38
  @patch("insightconnect_plugin_runtime.server.request_get")
34
39
  def test_cloud_plugin_calls_cps(self, mocked_req, _mock_cloud, _run):