diff --git a/docker.properties b/docker.properties index 707cd55e955a81f5fb4ef1a99b38d7a9d46ba043..ce42fc956dd4123376b6a16d6cdb33658ef6b9be 100644 --- a/docker.properties +++ b/docker.properties @@ -85,9 +85,6 @@ edu.nrao.carta.reverseProxyHost = black-abyss.nrao.edu edu.nrao.carta.redisPort = 6397 edu.nrao.carta.redisPassword = password -# Sentry.io Settings -edu.nrao.workspaces.SentrySettings.sentry_key = local - # WS Annihilator Settings edu.nrao.workspaces.AnnihilatorSettings.keepSpoolForDays = 10 edu.nrao.workspaces.AnnihilatorSettings.keepStagingForDays = 10 diff --git a/docs/requirements.txt b/docs/requirements.txt index 838cc87d57c226f5462725b51ec48e831d27624f..1d13d23e3f48f21086bcc671a90f580711ee4a71 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -36,7 +36,6 @@ prettierfier pytest requests scp -sentry_sdk simplejson sqlalchemy==1.4.46 tqdm diff --git a/services/capability/capability/server.py b/services/capability/capability/server.py index 841edd50990a6b044f13956cccd35b53d2dd7fbc..9bf8e35a418623fa387011687010fcce20c56f8e 100644 --- a/services/capability/capability/server.py +++ b/services/capability/capability/server.py @@ -17,7 +17,6 @@ # along with Workspaces. If not, see <https://www.gnu.org/licenses/>. import time -import sentry_sdk import sqlalchemy.orm import transaction import zope.sqlalchemy @@ -30,7 +29,6 @@ from pyramid.renderers import JSONP from pyramid.request import Request from pyramid.response import Response from pyramid_beaker import BeakerSessionFactoryConfig, session_factory_from_settings -from sentry_sdk.integrations.pyramid import PyramidIntegration from workspaces.capability.services.capability_info import CapabilityInfo from workspaces.capability.services.capability_service import ( @@ -46,6 +44,7 @@ from workspaces.workflow.services.workflow_service import WorkflowServiceRESTCli from .utility.notificationsender import TransactionalNotificationSender + # Copied from here: https://stackoverflow.com/questions/21107057/pyramid-cors-for-ajax-requests def add_cors_headers_response_callback(event: NewRequest): """ @@ -84,7 +83,9 @@ def add_cors_headers_response_callback(event: NewRequest): # --------------------------------------------------------- -def add_services(config: Configurator, session_factory: BeakerSessionFactoryConfig) -> Configurator: +def add_services( + config: Configurator, session_factory: BeakerSessionFactoryConfig +) -> Configurator: """ Add capability info, capability service, and workflow service to Pyramid request configuration @@ -95,10 +96,17 @@ def add_services(config: Configurator, session_factory: BeakerSessionFactoryConf # # these objects should exist across request boundaries workflow_rest_client = WorkflowServiceRESTClient() notification_rest_client = TransactionalNotificationSender(transaction.manager) - capability_info = CapabilityInfo(get_tm_session(session_factory, transaction.manager)) - state_machine_info = StateMachineInfo(get_tm_session(session_factory, transaction.manager)) + capability_info = CapabilityInfo( + get_tm_session(session_factory, transaction.manager) + ) + state_machine_info = StateMachineInfo( + get_tm_session(session_factory, transaction.manager) + ) capability_service = CapabilityService( - capability_info, state_machine_info, workflow_rest_client, notification_rest_client + capability_info, + state_machine_info, + workflow_rest_client, + notification_rest_client, ) archive_service = ArchiveService() @@ -128,13 +136,19 @@ def add_services(config: Configurator, session_factory: BeakerSessionFactoryConf ) # make capability launcher available for use in Pyramid - config.add_request_method(create_capability_launcher, "capability_launcher", reify=True) + config.add_request_method( + create_capability_launcher, "capability_launcher", reify=True + ) # make capability queue starter available for use in Pyramid - config.add_request_method(create_queue_restarter, "capability_queue_restarter", reify=True) + config.add_request_method( + create_queue_restarter, "capability_queue_restarter", reify=True + ) # make capability canceller available for use in Pyramid - config.add_request_method(create_capability_canceller, "capability_canceller", reify=True) + config.add_request_method( + create_capability_canceller, "capability_canceller", reify=True + ) # make workflow_info available for use in Pyramid config.add_request_method( @@ -144,15 +158,21 @@ def add_services(config: Configurator, session_factory: BeakerSessionFactoryConf reify=True, ) - config.add_request_method(lambda request: notification_rest_client, "notification_service", reify=True) + config.add_request_method( + lambda request: notification_rest_client, "notification_service", reify=True + ) # make archive_service available for use in Pyramid - config.add_request_method(lambda request: archive_service, "archive_service", reify=True) + config.add_request_method( + lambda request: archive_service, "archive_service", reify=True + ) return config def lookup_request(request): - return request.capability_info.lookup_capability_request(request.matchdict["request_id"]) + return request.capability_info.lookup_capability_request( + request.matchdict["request_id"] + ) # --------------------------------------------------------- @@ -163,7 +183,8 @@ def lookup_request(request): def get_tm_session( - session_factory: BeakerSessionFactoryConfig, transaction_manager: transaction.TransactionManager + session_factory: BeakerSessionFactoryConfig, + transaction_manager: transaction.TransactionManager, ) -> sqlalchemy.orm.Session: """ Enable Zope's transaction manager on our session @@ -183,7 +204,9 @@ def get_tm_session( # --------------------------------------------------------- # Prometheus logging -http_requests = Summary("http_request_timing", "HTTP Requests", ["status_code", "route_name"]) +http_requests = Summary( + "http_request_timing", "HTTP Requests", ["status_code", "route_name"] +) def prometheus_route_timing_factory(handler, registry): @@ -193,9 +216,9 @@ def prometheus_route_timing_factory(handler, registry): response = handler(request) end = time.time() if request.matched_route: - http_requests.labels(status_code=response.status_code, route_name=request.matched_route.name).observe( - end - start - ) + http_requests.labels( + status_code=response.status_code, route_name=request.matched_route.name + ).observe(end - start) return response return prometheus_route_timer @@ -211,7 +234,11 @@ class QueueReportCollector: self.capability_info = capability_info def collect(self): - c = CounterMetricFamily("capability_queue", "Queue status for each capability", labels=["capability", "status"]) + c = CounterMetricFamily( + "capability_queue", + "Queue status for each capability", + labels=["capability", "status"], + ) for report in self.capability_info.report(): c.add_metric([report.capability, "waiting"], report.waiting) @@ -228,18 +255,6 @@ class QueueReportCollector: def main(global_config, **settings): with Configurator(settings=settings) as config: - sentry_key = CapoConfig().settings("edu.nrao.workspaces.SentrySettings").sentry_key - - if sentry_key != "local": - sentry_sdk.init( - dsn=sentry_key, - integrations=[PyramidIntegration()], - # Set traces_sample_rate to 1.0 to capture 100% - # of transactions for performance monitoring. - # We recommend adjusting this value in production. - traces_sample_rate=1.0, - ) - # Helpers config.add_subscriber(add_cors_headers_response_callback, NewRequest) diff --git a/services/capability/poetry.lock b/services/capability/poetry.lock index 98ed222f6bb244065c7f5f5d7c4c1d1be0265e64..0569ac4ff6a222c7607492dde558360e2dfb664a 100644 --- a/services/capability/poetry.lock +++ b/services/capability/poetry.lock @@ -556,6 +556,16 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -1083,39 +1093,6 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] -[[package]] -name = "sentry-sdk" -version = "1.5.10" -description = "Python client for Sentry (https://sentry.io)" -optional = false -python-versions = "*" -files = [ - {file = "sentry-sdk-1.5.10.tar.gz", hash = "sha256:0a9eb20a84f4c17c08c57488d59fdad18669db71ebecb28fb0721423a33535f9"}, - {file = "sentry_sdk-1.5.10-py2.py3-none-any.whl", hash = "sha256:972c8fe9318a415b5cf35f687f568321472ef94b36806407c370ce9c88a67f2e"}, -] - -[package.dependencies] -certifi = "*" -urllib3 = ">=1.10.0" - -[package.extras] -aiohttp = ["aiohttp (>=3.5)"] -beam = ["apache-beam (>=2.12)"] -bottle = ["bottle (>=0.12.13)"] -celery = ["celery (>=3)"] -chalice = ["chalice (>=1.16.0)"] -django = ["django (>=1.8)"] -falcon = ["falcon (>=1.4)"] -flask = ["blinker (>=1.1)", "flask (>=0.11)"] -httpx = ["httpx (>=0.16.0)"] -pure-eval = ["asttokens", "executing", "pure-eval"] -pyspark = ["pyspark (>=2.4.4)"] -quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] -rq = ["rq (>=0.6)"] -sanic = ["sanic (>=0.8)"] -sqlalchemy = ["sqlalchemy (>=1.2)"] -tornado = ["tornado (>=5)"] - [[package]] name = "setuptools" version = "68.0.0" @@ -1447,4 +1424,4 @@ test = ["zope.testing"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "0330bcb9dc2a78fd69b90a5f52bb4c6256c58929d77315c05fb162cc19199081" +content-hash = "4fc1eea6d18754c3d0bf8ffa6af19a70dfbd34779b7ecd95543d019a0c7460b1" diff --git a/services/capability/pyproject.toml b/services/capability/pyproject.toml index bb89abc402cdd2a37c604d91383a69b7635ec61a..f2d42d06e65b3ba9c887b96231f393160f13dfab 100644 --- a/services/capability/pyproject.toml +++ b/services/capability/pyproject.toml @@ -24,7 +24,6 @@ sqlalchemy = "1.4.47" waitress = "^2.1.2" "zope.sqlalchemy" = "2.0" immutable-views = "^0.6.1" -sentry-sdk = "1.5.10" prometheus-client = "0.4.1" workspaces = {path = "../../shared/workspaces"} messaging = {path = "../../shared/messaging"} diff --git a/services/workflow/poetry.lock b/services/workflow/poetry.lock index 2fadcbbc9b483d5beacd8c4822c9cff8956a3da2..ec7d48f14ead2ba68e2f3b0780f5fab3fb1f4543 100644 --- a/services/workflow/poetry.lock +++ b/services/workflow/poetry.lock @@ -421,6 +421,16 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -848,38 +858,6 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] -[[package]] -name = "sentry-sdk" -version = "1.5.0" -description = "Python client for Sentry (https://sentry.io)" -optional = false -python-versions = "*" -files = [ - {file = "sentry-sdk-1.5.0.tar.gz", hash = "sha256:789a11a87ca02491896e121efdd64e8fd93327b69e8f2f7d42f03e2569648e88"}, - {file = "sentry_sdk-1.5.0-py2.py3-none-any.whl", hash = "sha256:0db297ab32e095705c20f742c3a5dac62fe15c4318681884053d0898e5abb2f6"}, -] - -[package.dependencies] -certifi = "*" -urllib3 = ">=1.10.0" - -[package.extras] -aiohttp = ["aiohttp (>=3.5)"] -beam = ["apache-beam (>=2.12)"] -bottle = ["bottle (>=0.12.13)"] -celery = ["celery (>=3)"] -chalice = ["chalice (>=1.16.0)"] -django = ["django (>=1.8)"] -falcon = ["falcon (>=1.4)"] -flask = ["blinker (>=1.1)", "flask (>=0.11)"] -httpx = ["httpx (>=0.16.0)"] -pure-eval = ["asttokens", "executing", "pure-eval"] -pyspark = ["pyspark (>=2.4.4)"] -rq = ["rq (>=0.6)"] -sanic = ["sanic (>=0.8)"] -sqlalchemy = ["sqlalchemy (>=1.2)"] -tornado = ["tornado (>=5)"] - [[package]] name = "setuptools" version = "68.0.0" @@ -1211,4 +1189,4 @@ test = ["zope.testing"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "2d9b5aac63d792b67046ca1fe2eca5a472e8379858a798e364fe4a451336dd07" +content-hash = "d7ad3ca6f57737f1b18e350b15663cb08907fdd65d41353f02018850d517caec" diff --git a/services/workflow/pyproject.toml b/services/workflow/pyproject.toml index 32c4e1039cc8996fd8da194872d984e901cda663..15f28d33c86c354eecbbb5cd432e33914201e0a4 100644 --- a/services/workflow/pyproject.toml +++ b/services/workflow/pyproject.toml @@ -20,7 +20,6 @@ workspaces = {path = "../../shared/workspaces"} messaging = {path = "../../shared/messaging"} "zope.sqlalchemy" = "2.0" immutable-views = "^0.6.1" -sentry-sdk = "1.5.0" prometheus-client = "0.4.1" psycopg2 = "^2.9.6" diff --git a/services/workflow/requirements.txt b/services/workflow/requirements.txt index 0bebaf31b506db9b75cfdca97c63badaf79dd8c5..ec6b80199d368fc65ab34a61b842dd961647c229 100644 --- a/services/workflow/requirements.txt +++ b/services/workflow/requirements.txt @@ -1,5 +1,3 @@ -# -e ../code/shared/messaging -# -e ../code/shared/workspaces -e ../code/apps/cli/utilities/wf_monitor -e ../code/apps/cli/utilities/aat_wrest -e ../code/apps/cli/utilities/contacts_wrest diff --git a/services/workflow/workflow/server.py b/services/workflow/workflow/server.py index 594bfb8ae202c0fb24c447aca30a21bbcd04e1ee..937807a3a6d9d01061c73c2b65fc3fc05dadf66e 100644 --- a/services/workflow/workflow/server.py +++ b/services/workflow/workflow/server.py @@ -26,7 +26,6 @@ from json import JSONDecodeError from pathlib import Path import prometheus_client -import sentry_sdk import transaction import zope.sqlalchemy from pycapo import CapoConfig @@ -37,7 +36,6 @@ from pyramid.response import FileResponse, Response from pyramid.view import view_config, view_defaults from pyramid.wsgi import wsgiapp from pyramid_beaker import session_factory_from_settings -from sentry_sdk.integrations.pyramid import PyramidIntegration from workspaces.system.schema import get_engine, get_session_factory from workspaces.workflow.schema import Workflow, WorkflowRequest, WorkflowRequestFile @@ -153,7 +151,9 @@ class WorkflowWorkingDirRestService: :return: None """ - requested_workflow = self.request.info.lookup_workflow_request(self.request.matchdict["request_id"]) + requested_workflow = self.request.info.lookup_workflow_request( + self.request.matchdict["request_id"] + ) results_path = requested_workflow.results_dir parent_paths = [requested_workflow.results_dir] @@ -171,11 +171,15 @@ class WorkflowWorkingDirRestService: @view_config(request_method="GET", route_name="serve_weblog", renderer="json") def serve_weblog(self): - requested_workflow = self.request.info.lookup_workflow_request(self.request.matchdict["request_id"]) + requested_workflow = self.request.info.lookup_workflow_request( + self.request.matchdict["request_id"] + ) results_path = Path(requested_workflow.results_dir) index_path_list = list(results_path.glob("products/pipeline-*/html/index.html")) - failed_weblog_path_list = list(results_path.glob("working/pipeline-*/html/index.html")) + failed_weblog_path_list = list( + results_path.glob("working/pipeline-*/html/index.html") + ) if index_path_list and len(index_path_list) == 1: index_path = index_path_list[0] elif failed_weblog_path_list and len(failed_weblog_path_list) == 1: @@ -195,10 +199,14 @@ class WorkflowWorkingDirRestService: @view_config(request_method="GET", route_name="get_qa_notes", renderer="json") def get_qa_notes(self): - requested_workflow = self.request.info.lookup_workflow_request(self.request.matchdict["request_id"]) + requested_workflow = self.request.info.lookup_workflow_request( + self.request.matchdict["request_id"] + ) results_path = Path(requested_workflow.results_dir) - qa_notes_list = list(results_path.glob("products/pipeline-*/html/qa_notes.html")) + qa_notes_list = list( + results_path.glob("products/pipeline-*/html/qa_notes.html") + ) if qa_notes_list and len(qa_notes_list) == 1: qa_notes_path = qa_notes_list[0] else: @@ -213,14 +221,20 @@ class WorkflowWorkingDirRestService: with open(qa_notes_path, "r") as qa_notes: qa_notes_text = qa_notes.read() - return Response(status_int=http.HTTPStatus.OK, json_body={"resp": f"{qa_notes_text}"}) + return Response( + status_int=http.HTTPStatus.OK, json_body={"resp": f"{qa_notes_text}"} + ) @view_config(request_method="POST", route_name="get_qa_notes", renderer="json") def save_qa_notes(self): - requested_workflow = self.request.info.lookup_workflow_request(self.request.matchdict["request_id"]) + requested_workflow = self.request.info.lookup_workflow_request( + self.request.matchdict["request_id"] + ) results_path = Path(requested_workflow.results_dir) - qa_notes_list = list(results_path.glob("products/pipeline-*/html/qa_notes.html")) + qa_notes_list = list( + results_path.glob("products/pipeline-*/html/qa_notes.html") + ) if qa_notes_list and len(qa_notes_list) == 1: qa_notes_path = qa_notes_list[0] else: @@ -235,15 +249,21 @@ class WorkflowWorkingDirRestService: # sanitize input before writing/persisting # \\u0000 is an invalid character that is incompatible with postgres json columns # from StackOverflow: https://stackoverflow.com/questions/63092267/how-to-handle-api-responsesjson-containing-x00-or-u0000-in-its-data-and-s - edits = json.loads(json.dumps(self.request.json_body["edits"])).replace("\\u0000", "") + edits = json.loads(json.dumps(self.request.json_body["edits"])).replace( + "\\u0000", "" + ) qa_notes.write(edits) return Response( status_int=http.HTTPStatus.OK, - json_body={"resp": f"Edits made to QA notes file in workflow {self.request.matchdict['request_id']}."}, + json_body={ + "resp": f"Edits made to QA notes file in workflow {self.request.matchdict['request_id']}." + }, ) - @view_config(request_method="GET", route_name="serve_carta_wrapper", renderer="json") + @view_config( + request_method="GET", route_name="serve_carta_wrapper", renderer="json" + ) def serve_carta_wrapper(self): """ Dish up some HTML containing the CARTA URL in a frame. @@ -251,9 +271,13 @@ class WorkflowWorkingDirRestService: :return: """ - path = Path(f"/lustre/aoc/cluster/pipeline/docker/workspaces/html/{self.request.matchdict['request_id']}") + path = Path( + f"/lustre/aoc/cluster/pipeline/docker/workspaces/html/{self.request.matchdict['request_id']}" + ) carta_html_file = list(path.iterdir())[0] - return FileResponse(carta_html_file, request=self.request, content_type="text/html") + return FileResponse( + carta_html_file, request=self.request, content_type="text/html" + ) @view_config(route_name="get_healthcheck", renderer="json") def get_healthcheck(self) -> Response: @@ -263,7 +287,10 @@ class WorkflowWorkingDirRestService: :return: """ return Response( - status=http.HTTPStatus.OK, json_body={"healthcheck": f"Workflow service returned {http.HTTPStatus.OK}"} + status=http.HTTPStatus.OK, + json_body={ + "healthcheck": f"Workflow service returned {http.HTTPStatus.OK}" + }, ) def generate_working_directory_dict(self, results_path, parent_paths) -> dict: @@ -290,7 +317,13 @@ class WorkflowWorkingDirRestService: # check if url needs a slash to divide paths divider = ("/", "")[self.request.current_route_url().endswith("/")] - content_key.update({key.name: {"url": self.request.current_route_url() + divider + key.name}}) + content_key.update( + { + key.name: { + "url": self.request.current_route_url() + divider + key.name + } + } + ) # add full path for content content_key[key.name].update({"full_path": key.absolute().__str__()}) @@ -306,7 +339,9 @@ class WorkflowWorkingDirRestService: # if it is a directory, create a json object workdir_json = json.dumps(workdir_dict, indent=2) # create response with the json object as the body - response = Response(body=workdir_json, request=self.request, content_type="text/json") + response = Response( + body=workdir_json, request=self.request, content_type="text/json" + ) else: # if it is not a directory, serve the static file response = FileResponse( @@ -317,11 +352,15 @@ class WorkflowWorkingDirRestService: def generate_url_from_path(self, root_path, results_path): current_url = self.request.current_route_url() - return current_url.replace("/weblog", "/dir") + root_path.replace(results_path, "") + return current_url.replace("/weblog", "/dir") + root_path.replace( + results_path, "" + ) def generate_qa_notes_path(self, root_path, results_path): current_url = self.request.current_route_url() - return current_url.replace("/qa_notes", "/dir") + root_path.replace(results_path, "") + return current_url.replace("/qa_notes", "/dir") + root_path.replace( + results_path, "" + ) @view_defaults(route_name="workflow_request", renderer="json") @@ -353,7 +392,9 @@ class WorkflowRequestRestService: # Most common case: Empty body for simple requests, continue with an empty dict argument_body = {} - return self.request.info.create_workflow_request(self.request.context.workflow_name, argument_body) + return self.request.info.create_workflow_request( + self.request.context.workflow_name, argument_body + ) @view_config(request_method="POST", route_name="submit_workflow_request") def submit_workflow(self): @@ -393,7 +434,11 @@ class WorkflowRequestRestService: file = lookup_file(request=self.request) # 2. create ingestion workflow request - ingest_type = "ingest_cal" if "calibration" in self.request.matchdict["name"] else "ingest_image" + ingest_type = ( + "ingest_cal" + if "calibration" in self.request.matchdict["name"] + else "ingest_image" + ) ingest_request = self.request.info.create_workflow_request( workflow=ingest_type, argument={"parent_wf_request_id": self.request.matchdict["request_id"]}, @@ -419,7 +464,9 @@ class WorkflowRequestRestService: argument=self.request.json_body, ) self.request.workflows.execute(ingestion_request) - return Response(status_code=http.HTTPStatus.OK, json_body=ingestion_request.__json__()) + return Response( + status_code=http.HTTPStatus.OK, json_body=ingestion_request.__json__() + ) @view_config(request_method="POST", route_name="abort_workflow_request") def abort(self): @@ -500,10 +547,16 @@ class WorkflowRequestRestService: """ body = self.request.json_body - identifier = int(body["request_id"]) if "request_id" in body else body["project_code"] + identifier = ( + int(body["request_id"]) if "request_id" in body else body["project_code"] + ) msg_type = self.request.matchdict["msg_type"] - additional = body["project_code"] if "project_code" in body and identifier != body["project_code"] else None + additional = ( + body["project_code"] + if "project_code" in body and identifier != body["project_code"] + else None + ) self.request.workflows.message_archive(identifier, msg_type, additional) return Response( @@ -538,16 +591,22 @@ class WorkflowRequestRestService: request_id = self.request.matchdict["request_id"] self.request.workflows.send_forced_fail(request_id) - @view_config(request_method="GET", route_name="workflow_request_htcondor_id", renderer="json") + @view_config( + request_method="GET", route_name="workflow_request_htcondor_id", renderer="json" + ) def get_request_htcondor_id(self): """ Pyramid view that gives back the HTCondor job ID for a given workflow request :return: HTTP response with HTCondor job ID in the body """ - requested_workflow = self.request.info.lookup_workflow_request(self.request.matchdict["request_id"]) + requested_workflow = self.request.info.lookup_workflow_request( + self.request.matchdict["request_id"] + ) - return Response(json_body={"htcondor_job_id": str(requested_workflow.htcondor_job_id)}) + return Response( + json_body={"htcondor_job_id": str(requested_workflow.htcondor_job_id)} + ) @view_config(request_method="GET", route_name="list_stale_requests") def get_stale_requests(self): @@ -688,20 +747,23 @@ def get_tm_session(session_factory, transaction_manager): http_requests = None + def prometheus_route_timing_factory(handler, registry): # if timing support is enabled, return a wrapper global http_requests # Prometheus logging - http_requests = prometheus_client.Summary("http_request_timing", "HTTP Requests", ["status_code", "route_name"]) + http_requests = prometheus_client.Summary( + "http_request_timing", "HTTP Requests", ["status_code", "route_name"] + ) def prometheus_route_timer(request): start = time.time() response = handler(request) end = time.time() if request.matched_route: - http_requests.labels(status_code=response.status_code, route_name=request.matched_route.name).observe( - end - start - ) + http_requests.labels( + status_code=response.status_code, route_name=request.matched_route.name + ).observe(end - start) return response return prometheus_route_timer @@ -716,18 +778,6 @@ def prometheus_route_timing_factory(handler, registry): def main(global_config, **settings): with Configurator(settings=settings) as config: - sentry_key = CapoConfig().settings("edu.nrao.workspaces.SentrySettings").sentry_key - - if sentry_key != "local": - sentry_sdk.init( - dsn=sentry_key, - integrations=[PyramidIntegration()], - # Set traces_sample_rate to 1.0 to capture 100% - # of transactions for performance monitoring. - # We recommend adjusting this value in production. - traces_sample_rate=1.0, - ) - session_factory = session_factory_from_settings(settings) config.set_session_factory(session_factory) config.add_renderer("jsonp", JSONP(param_name="callback")) @@ -745,7 +795,9 @@ def main(global_config, **settings): # we need to build a workflow_info here for the message handler, but # we won't use it anywhere else, we will make new ones per-request - workflow_info = WorkflowInfo(get_tm_session(session_factory, transaction.manager)) + workflow_info = WorkflowInfo( + get_tm_session(session_factory, transaction.manager) + ) message_handler = WorkflowMessageHandler(workflow_info) workflow_recover = MonitorRecover(workflow_info) @@ -757,7 +809,9 @@ def main(global_config, **settings): # make workflow_service available for use in Pyramid config.add_request_method( - lambda r: WorkflowService(r.info, message_handler, workflow_recover), "workflows", reify=True + lambda r: WorkflowService(r.info, message_handler, workflow_recover), + "workflows", + reify=True, ) # GET /workflows <- list of workflows diff --git a/test-requirements.txt b/test-requirements.txt index 8166c668c422882fc82f2bef451a44c50735f20e..29fba01387cc84a517a9933eafe706e153e5ac4f 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -17,7 +17,6 @@ pyramid-tm==2.5 immutable_views hypothesis kombu -sentry_sdk zope-sqlalchemy==2.0 -e ./shared/messaging -e ./shared/workspaces