insightconnect-plugin-runtime 5.4.1__py3-none-any.whl → 5.4.2__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.
- insightconnect_plugin_runtime/server.py +1 -21
- {insightconnect_plugin_runtime-5.4.1.dist-info → insightconnect_plugin_runtime-5.4.2.dist-info}/METADATA +3 -3
- {insightconnect_plugin_runtime-5.4.1.dist-info → insightconnect_plugin_runtime-5.4.2.dist-info}/RECORD +6 -6
- tests/unit/test_server_cloud_plugins.py +8 -22
- {insightconnect_plugin_runtime-5.4.1.dist-info → insightconnect_plugin_runtime-5.4.2.dist-info}/WHEEL +0 -0
- {insightconnect_plugin_runtime-5.4.1.dist-info → insightconnect_plugin_runtime-5.4.2.dist-info}/top_level.txt +0 -0
|
@@ -6,8 +6,6 @@ import logging
|
|
|
6
6
|
from apispec import APISpec
|
|
7
7
|
from apispec.ext.marshmallow import MarshmallowPlugin
|
|
8
8
|
from apispec_webframeworks.flask import FlaskPlugin
|
|
9
|
-
from apscheduler.schedulers.background import BackgroundScheduler
|
|
10
|
-
|
|
11
9
|
|
|
12
10
|
from flask import Flask, request_started, request
|
|
13
11
|
import gunicorn.app.base
|
|
@@ -108,7 +106,7 @@ class PluginServer(gunicorn.app.base.BaseApplication):
|
|
|
108
106
|
self.workers = workers
|
|
109
107
|
self.threads = threads
|
|
110
108
|
# initialise before reaching out to CPS for configured values
|
|
111
|
-
self.config_options
|
|
109
|
+
self.config_options = {}
|
|
112
110
|
self.get_plugin_properties_from_cps()
|
|
113
111
|
self.app, self.blueprints = self.create_flask_app()
|
|
114
112
|
|
|
@@ -195,7 +193,6 @@ class PluginServer(gunicorn.app.base.BaseApplication):
|
|
|
195
193
|
plugin_config = resp_json.get("plugins", {}).get(plugin, {})
|
|
196
194
|
|
|
197
195
|
self.config_options = plugin_config
|
|
198
|
-
self.schedule_interval = resp_json.get("config", {}).get("interval", DEFAULT_SCHEDULE_INTERVAL_MINUTES)
|
|
199
196
|
self.logger.info("Plugin configuration successfully retrieved...")
|
|
200
197
|
break
|
|
201
198
|
except MissingSchema as missing_schema:
|
|
@@ -260,21 +257,6 @@ class PluginServer(gunicorn.app.base.BaseApplication):
|
|
|
260
257
|
blueprint, url_prefix=VERSION_MAPPING[blueprint.name]
|
|
261
258
|
)
|
|
262
259
|
|
|
263
|
-
def register_scheduled_tasks(self):
|
|
264
|
-
# Once Flask server is up and running also start the get_plugin_configs
|
|
265
|
-
try:
|
|
266
|
-
if self.plugin.tasks and self.schedule_interval:
|
|
267
|
-
self.logger.info(f"Starting scheduled task(s) to run at interval of {self.schedule_interval} minutes...")
|
|
268
|
-
scheduler = BackgroundScheduler(daemon=True)
|
|
269
|
-
scheduler.add_job(self.get_plugin_properties_from_cps, 'interval', minutes=self.schedule_interval)
|
|
270
|
-
scheduler.start()
|
|
271
|
-
else:
|
|
272
|
-
reason = "No tasks found found within plugin," if self.schedule_interval else "No schedule defined,"
|
|
273
|
-
self.logger.info(f"{reason} not starting scheduled tasks...")
|
|
274
|
-
except Exception as exception:
|
|
275
|
-
self.logger.error("Unable to start up scheduler, plugin will not be refreshing these configuration values."
|
|
276
|
-
f"Error={exception}", exc_info=True)
|
|
277
|
-
|
|
278
260
|
@staticmethod
|
|
279
261
|
def bind_request_details(sender: Flask, **extras) -> None:
|
|
280
262
|
"""
|
|
@@ -306,8 +288,6 @@ class PluginServer(gunicorn.app.base.BaseApplication):
|
|
|
306
288
|
try:
|
|
307
289
|
self.register_blueprint()
|
|
308
290
|
self.register_api_spec()
|
|
309
|
-
if is_running_in_cloud():
|
|
310
|
-
self.register_scheduled_tasks()
|
|
311
291
|
self.arbiter.run()
|
|
312
292
|
except RuntimeError as e:
|
|
313
293
|
sys.stderr.write("\nError: %s\n" % e)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: insightconnect-plugin-runtime
|
|
3
|
-
Version: 5.4.
|
|
3
|
+
Version: 5.4.2
|
|
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,7 +26,6 @@ Requires-Dist: apispec-webframeworks ==1.0.0
|
|
|
26
26
|
Requires-Dist: blinker ==1.7.0
|
|
27
27
|
Requires-Dist: structlog ==24.1.0
|
|
28
28
|
Requires-Dist: python-json-logger ==2.0.7
|
|
29
|
-
Requires-Dist: APScheduler ==3.10.4
|
|
30
29
|
|
|
31
30
|
|
|
32
31
|
# InsightConnect Python Plugin Runtime 
|
|
@@ -186,7 +185,7 @@ Running a specific test file:
|
|
|
186
185
|
| | Plugin | Slim Plugin |
|
|
187
186
|
|:------------------|:-------:|:-----------:|
|
|
188
187
|
| Python Version | 3.9.18 | 3.9.18 |
|
|
189
|
-
| OS | Alpine |
|
|
188
|
+
| OS | Alpine | Bullseye |
|
|
190
189
|
| Package installer | apk | apt |
|
|
191
190
|
| Shell | /bin/sh | /bin/bash |
|
|
192
191
|
| Image Size | ~350MB | ~180MB |
|
|
@@ -213,6 +212,7 @@ after cloning this repository.
|
|
|
213
212
|
|
|
214
213
|
## Changelog
|
|
215
214
|
|
|
215
|
+
* 5.4.2 - Remove background scheduler to simplify when the server gets custom_config values | Revert to use `bullseye` for slim image.
|
|
216
216
|
* 5.4.1 - Retry logic added to get values from external API for custom_config values.
|
|
217
217
|
* 5.4.0 - Implementation of custom_config parameter for plugin tasks | Alpine image updated OpenSSL and expat | Use `bookworm` for slim image.
|
|
218
218
|
* 5.3.2 - Updated OpenSSL in alpine image and core packages to latest versions.
|
|
@@ -8,7 +8,7 @@ insightconnect_plugin_runtime/helper.py,sha256=m5PxN04-NPXM1X10S2wwjqmiLvnNntd6T
|
|
|
8
8
|
insightconnect_plugin_runtime/metrics.py,sha256=hf_Aoufip_s4k4o8Gtzz90ymZthkaT2e5sXh5B4LcF0,3186
|
|
9
9
|
insightconnect_plugin_runtime/plugin.py,sha256=A6GMrYTNFpyCeEP00jRbp89Xu6KdQFq0HOVWV2AxA6Q,22969
|
|
10
10
|
insightconnect_plugin_runtime/schema.py,sha256=jTNc6KAMqFpaDVWrAYhkVC6e8I63P3X7uVlJkAr1hiY,583
|
|
11
|
-
insightconnect_plugin_runtime/server.py,sha256=
|
|
11
|
+
insightconnect_plugin_runtime/server.py,sha256=vm0fJcnaI9Z3WYB37wUFkOjcym_kMlSm3lAyFP3ARiM,11771
|
|
12
12
|
insightconnect_plugin_runtime/step.py,sha256=KdERg-789-s99IEKN61DR08naz-YPxyinPT0C_T81C4,855
|
|
13
13
|
insightconnect_plugin_runtime/task.py,sha256=d-H1EAzVnmSdDEJtXyIK5JySprxpF9cetVoFGtWlHrg,123
|
|
14
14
|
insightconnect_plugin_runtime/trigger.py,sha256=Zq3cy68N3QxAGbNZKCID6CZF05Zi7YD2sdy_qbedUY8,874
|
|
@@ -73,12 +73,12 @@ tests/unit/test_metrics.py,sha256=PjjTrB9w7uQ2Q5UN-893-SsH3EGJuBseOMHSD1I004s,79
|
|
|
73
73
|
tests/unit/test_oauth.py,sha256=nbFG0JH1x04ExXqSe-b5BGdt_hJs7DP17eUa6bQzcYI,2093
|
|
74
74
|
tests/unit/test_plugin.py,sha256=ZTNAZWwZhDIAbxkVuWhnz9FzmojbijgMmsLWM2mXQI0,4160
|
|
75
75
|
tests/unit/test_schema.py,sha256=swWZPRo_Q4M6VHte-srmxcV2wH-XS7pgmNRxpaL0Qrg,642
|
|
76
|
-
tests/unit/test_server_cloud_plugins.py,sha256=
|
|
76
|
+
tests/unit/test_server_cloud_plugins.py,sha256=GvJCOgpaxw3jOz_ABCkui_Ymai0kAZK1MqR0ldERnhw,4544
|
|
77
77
|
tests/unit/test_server_spec.py,sha256=je97BaktgK0Fiz3AwFPkcmHzYtOJJNqJV_Fw5hrvqX4,644
|
|
78
78
|
tests/unit/test_trigger.py,sha256=E53mAUoVyponWu_4IQZ0IC1gQ9lakBnTn_9vKN2IZfg,1692
|
|
79
79
|
tests/unit/test_variables.py,sha256=OUEOqGYZA3Nd5oKk5GVY3hcrWKHpZpxysBJcO_v5gzs,291
|
|
80
80
|
tests/unit/utils.py,sha256=-GB3nz4YJ2nMm0AR7Fm4lCNJMHCWOmLtNI1N4b9hRdo,470
|
|
81
|
-
insightconnect_plugin_runtime-5.4.
|
|
82
|
-
insightconnect_plugin_runtime-5.4.
|
|
83
|
-
insightconnect_plugin_runtime-5.4.
|
|
84
|
-
insightconnect_plugin_runtime-5.4.
|
|
81
|
+
insightconnect_plugin_runtime-5.4.2.dist-info/METADATA,sha256=8h98fhwRRCE9c7y4U6-96Qw7ci0h89KqvnrS2r-XPg0,12472
|
|
82
|
+
insightconnect_plugin_runtime-5.4.2.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
83
|
+
insightconnect_plugin_runtime-5.4.2.dist-info/top_level.txt,sha256=AJtyJOpiFzHxsbHUICTcUKXyrGQ3tZxhrEHsPjJBvEA,36
|
|
84
|
+
insightconnect_plugin_runtime-5.4.2.dist-info/RECORD,,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from requests.exceptions import HTTPError, Timeout, TooManyRedirects
|
|
2
2
|
from parameterized import parameterized
|
|
3
|
-
from unittest import TestCase
|
|
3
|
+
from unittest import TestCase, skip
|
|
4
4
|
from unittest.mock import patch, MagicMock
|
|
5
5
|
|
|
6
|
-
from insightconnect_plugin_runtime.server import PluginServer
|
|
6
|
+
from insightconnect_plugin_runtime.server import PluginServer
|
|
7
7
|
from tests.plugin.hello_world import KomandHelloWorld
|
|
8
8
|
from .utils import MockResponse, Logger
|
|
9
9
|
|
|
@@ -18,26 +18,20 @@ class TestServerCloudPlugins(TestCase):
|
|
|
18
18
|
self.plugin_name = self.plugin.name.lower().replace(" ", "_")
|
|
19
19
|
|
|
20
20
|
@parameterized.expand([["Set cloud to false", False], ["Set cloud to true", True]])
|
|
21
|
-
@patch("insightconnect_plugin_runtime.server.PluginServer.register_scheduled_tasks")
|
|
22
21
|
@patch("insightconnect_plugin_runtime.server.request_get")
|
|
23
|
-
def test_cloud_plugin_no_tasks_ignore_cps(self, _test_name, cloud, mocked_req,
|
|
22
|
+
def test_cloud_plugin_no_tasks_ignore_cps(self, _test_name, cloud, mocked_req, mock_cloud, _run):
|
|
24
23
|
mock_cloud.return_value = cloud # Mock plugin running in cloud vs not
|
|
25
24
|
self.plugin.tasks = None # ensure still no tasks as other tests edit this and could fail before reverting
|
|
26
25
|
plugin_server = PluginServer(self.plugin) # this plugin has no tasks by default
|
|
27
26
|
|
|
28
27
|
plugin_server.start()
|
|
29
28
|
self.assertEqual(plugin_server.config_options, {})
|
|
30
|
-
self.assertEqual(plugin_server.schedule_interval, None)
|
|
31
|
-
|
|
32
|
-
# Depending on if we're running cloud or not this could be called but we only reach to CPS if we have tasks
|
|
33
|
-
self.assertEquals(mocked_scheduler.called, cloud)
|
|
34
29
|
|
|
35
30
|
# Plugin server never calls out to CPS as either we are not running in cloud mode or have no tasks.
|
|
36
31
|
self.assertFalse(mocked_req.called)
|
|
37
32
|
|
|
38
|
-
@patch("insightconnect_plugin_runtime.server.PluginServer.register_scheduled_tasks")
|
|
39
33
|
@patch("insightconnect_plugin_runtime.server.request_get")
|
|
40
|
-
def test_cloud_plugin_calls_cps(self, mocked_req,
|
|
34
|
+
def test_cloud_plugin_calls_cps(self, mocked_req, _mock_cloud, _run):
|
|
41
35
|
mocked_req.return_value = MockResponse({"plugins":{self.plugin_name: PLUGIN_VALUE_1, 'plugin': PLUGIN_VALUE_2},
|
|
42
36
|
"config": {"interval": 25}})
|
|
43
37
|
self.plugin.tasks = 'fake tasks' # this plugin by default has no tasks so force it to have some
|
|
@@ -49,19 +43,15 @@ class TestServerCloudPlugins(TestCase):
|
|
|
49
43
|
|
|
50
44
|
# We only save the plugin config for the current config and ignore `other_plugin`
|
|
51
45
|
self.assertDictEqual(plugin_server.config_options, PLUGIN_VALUE_1)
|
|
52
|
-
self.assertEqual(plugin_server.schedule_interval, SCHEDULE_INTERVAL)
|
|
53
46
|
|
|
54
|
-
# We should now schedule this to run
|
|
55
|
-
self.assertEquals(mocked_scheduler.called, True)
|
|
56
47
|
self.plugin.tasks = None # reset tasks value
|
|
57
48
|
|
|
58
49
|
@parameterized.expand([["error", HTTPError], ["error", Timeout], ["unexpected", TooManyRedirects]])
|
|
59
|
-
@patch("insightconnect_plugin_runtime.server.PluginServer.register_scheduled_tasks")
|
|
60
50
|
@patch("insightconnect_plugin_runtime.server.request_get")
|
|
61
51
|
@patch("structlog.get_logger")
|
|
62
52
|
@patch("insightconnect_plugin_runtime.server.CPS_RETRY", new=2) # reduce retries in unit tests
|
|
63
53
|
@patch("insightconnect_plugin_runtime.server.RETRY_SLEEP", new=1) # reduce sleep in unit tests
|
|
64
|
-
def
|
|
54
|
+
def test_cps_raises_an_error(self, test_cond, exception, log, mocked_req, _mock_cloud, _run):
|
|
65
55
|
log.return_value = Logger()
|
|
66
56
|
# If we have successfully got config and scheduler options, and later this call fails we should keep values
|
|
67
57
|
mocked_req.return_value = MockResponse({"plugins": {self.plugin_name: PLUGIN_VALUE_1, 'plugin': PLUGIN_VALUE_2},
|
|
@@ -72,9 +62,8 @@ class TestServerCloudPlugins(TestCase):
|
|
|
72
62
|
plugin_server.start()
|
|
73
63
|
|
|
74
64
|
self.assertDictEqual(plugin_server.config_options, PLUGIN_VALUE_2)
|
|
75
|
-
self.assertEqual(plugin_server.schedule_interval, DEFAULT_SCHEDULE_INTERVAL_MINUTES) # no resp uses default
|
|
76
65
|
|
|
77
|
-
# First call has happened and now successful - force
|
|
66
|
+
# First call has happened and now successful - force to hit specific handled and unexpected errors.
|
|
78
67
|
mocked_req.side_effect = exception("Warning HTTP error returned...")
|
|
79
68
|
plugin_server.get_plugin_properties_from_cps()
|
|
80
69
|
# we log error in all and `unexpected` in TooManyRedirects as there is no direct catch for this
|
|
@@ -82,16 +71,13 @@ class TestServerCloudPlugins(TestCase):
|
|
|
82
71
|
|
|
83
72
|
# Values should not have changed
|
|
84
73
|
self.assertDictEqual(plugin_server.config_options, PLUGIN_VALUE_2)
|
|
85
|
-
self.assertEqual(plugin_server.schedule_interval, DEFAULT_SCHEDULE_INTERVAL_MINUTES)
|
|
86
74
|
|
|
87
|
-
# Next schedule returns
|
|
88
|
-
|
|
89
|
-
mocked_req.return_value = MockResponse({"config": {"interval": new_schedule}})
|
|
75
|
+
# Next schedule returns no configurations for plugins
|
|
76
|
+
mocked_req.return_value = MockResponse({})
|
|
90
77
|
mocked_req.side_effect = None
|
|
91
78
|
plugin_server.get_plugin_properties_from_cps()
|
|
92
79
|
|
|
93
80
|
# And this new values are now updated for the plugin server
|
|
94
81
|
self.assertDictEqual(plugin_server.config_options, {})
|
|
95
|
-
self.assertEqual(plugin_server.schedule_interval, new_schedule)
|
|
96
82
|
|
|
97
83
|
self.plugin.tasks = None # reset tasks value
|
|
File without changes
|
|
File without changes
|