diff --git a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py index 479c41bab..53f102a72 100644 --- a/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py +++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_client.py @@ -85,3 +85,13 @@ class MonkeyIslandClient(object): def is_all_monkeys_dead(self): query = {'dead': False} return len(self.find_monkeys_in_db(query)) == 0 + + def clear_caches(self): + """ + Tries to clear caches. + :raises: If error (by error code), raises the error + :return: The response + """ + response = self.requests.delete("api/test/clear_caches") + response.raise_for_status() + return response diff --git a/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py b/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py index 388115463..23f259a9c 100644 --- a/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py +++ b/envs/monkey_zoo/blackbox/island_client/monkey_island_requests.py @@ -61,6 +61,14 @@ class MonkeyIslandRequests(object): headers=self.get_jwt_header(), verify=False) + @_Decorators.refresh_jwt_token + def delete(self, url): + return requests.delete( # noqa: DOU123 + self.addr + url, + headers=self.get_jwt_header(), + verify=False + ) + @_Decorators.refresh_jwt_token def get_jwt_header(self): return {"Authorization": "JWT " + self.token} diff --git a/monkey/monkey_island/cc/app.py b/monkey/monkey_island/cc/app.py index be2430dda..2af0e2230 100644 --- a/monkey/monkey_island/cc/app.py +++ b/monkey/monkey_island/cc/app.py @@ -25,6 +25,7 @@ from monkey_island.cc.resources.root import Root from monkey_island.cc.resources.telemetry import Telemetry from monkey_island.cc.resources.telemetry_feed import TelemetryFeed from monkey_island.cc.resources.pba_file_download import PBAFileDownload +from monkey_island.cc.resources.test.clear_caches import ClearCaches from monkey_island.cc.resources.version_update import VersionUpdate from monkey_island.cc.resources.pba_file_upload import FileUpload from monkey_island.cc.resources.attack.attack_config import AttackConfiguration @@ -116,6 +117,7 @@ def init_api_resources(api): api.add_resource(VersionUpdate, '/api/version-update', '/api/version-update/') api.add_resource(MonkeyTest, '/api/test/monkey') + api.add_resource(ClearCaches, '/api/test/clear_caches') api.add_resource(LogTest, '/api/test/log') diff --git a/monkey/monkey_island/cc/resources/test/__init__.py b/monkey/monkey_island/cc/resources/test/__init__.py index 28550f830..aa3c993b0 100644 --- a/monkey/monkey_island/cc/resources/test/__init__.py +++ b/monkey/monkey_island/cc/resources/test/__init__.py @@ -1,4 +1,4 @@ """ This package contains resources used by blackbox tests -to analize test results, download logs and so on. +to analyze test results, download logs and so on. """ diff --git a/monkey/monkey_island/cc/resources/test/clear_caches.py b/monkey/monkey_island/cc/resources/test/clear_caches.py new file mode 100644 index 000000000..e23629a18 --- /dev/null +++ b/monkey/monkey_island/cc/resources/test/clear_caches.py @@ -0,0 +1,35 @@ +import logging + +import flask_restful + +from monkey_island.cc.auth import jwt_required +from monkey_island.cc.services.attack.attack_report import AttackReportService +from monkey_island.cc.services.reporting.report import ReportService + +NOT_ALL_REPORTS_DELETED = "Not all reports have been cleared from the DB!" + +logger = logging.getLogger(__name__) + + +class ClearCaches(flask_restful.Resource): + """ + Used for timing tests - we want to get actual execution time of functions in BlackBox without caching - so we use this + to clear the caches. + :note: DO NOT CALL THIS IN PRODUCTION CODE as this will slow down the user experience. + """ + @jwt_required() + def delete(self, **kw): + try: + logger.warning("Trying to clear caches! Make sure this is not production") + ReportService.delete_saved_report_if_exists() + AttackReportService.delete_saved_report_if_exists() + # TODO: Monkey.clear_caches(), clear LRU caches of function in the Monkey object + except RuntimeError as e: + logger.exception(e) + flask_restful.abort(500, error_info=str(e)) + + if ReportService.is_report_generated() or AttackReportService.is_report_generated(): + logger.exception(NOT_ALL_REPORTS_DELETED) + flask_restful.abort(500, error_info=NOT_ALL_REPORTS_DELETED) + + return {"success": "true"} diff --git a/monkey/monkey_island/cc/services/attack/attack_report.py b/monkey/monkey_island/cc/services/attack/attack_report.py index 43cca0876..2f1e9dcf8 100644 --- a/monkey/monkey_island/cc/services/attack/attack_report.py +++ b/monkey/monkey_island/cc/services/attack/attack_report.py @@ -103,3 +103,11 @@ class AttackReportService: """ generated_report = mongo.db.attack_report.find_one({}) return generated_report is not None + + @staticmethod + def delete_saved_report_if_exists(): + if AttackReportService.is_report_generated(): + latest_report = mongo.db.attack_report.find_one({'name': REPORT_NAME}) + delete_result = mongo.db.report.delete_one({"_id": latest_report['_id']}) + if delete_result.deleted_count != 1: + raise RuntimeError("Error while deleting report:" + str(delete_result)) diff --git a/monkey/monkey_island/cc/services/reporting/report.py b/monkey/monkey_island/cc/services/reporting/report.py index 97e8fa4f1..f96279dd6 100644 --- a/monkey/monkey_island/cc/services/reporting/report.py +++ b/monkey/monkey_island/cc/services/reporting/report.py @@ -773,6 +773,19 @@ class ReportService: return False + @staticmethod + def delete_saved_report_if_exists(): + """ + This function clears the saved report from the DB. + :raises RuntimeError if deletion failed + """ + latest_report_doc = mongo.db.report.find_one({}, {'meta.latest_monkey_modifytime': 1}) + + if latest_report_doc: + delete_result = mongo.db.report.delete_one({"_id": latest_report_doc['_id']}) + if delete_result.deleted_count != 1: + raise RuntimeError("Error while deleting report:" + str(delete_result)) + @staticmethod def decode_dot_char_before_mongo_insert(report_dict): """