Skip to content
Snippets Groups Projects
Commit c2e23ed8 authored by Janet Goldstein's avatar Janet Goldstein
Browse files

WS-640: Replace CARTA URL with link to wrapper page

parent ad4b9f74
No related branches found
No related tags found
1 merge request!475WS-640: Replace CARTA URL with link to wrapper page
Pipeline #2768 passed
......@@ -5,9 +5,14 @@ import os
import sys
from pathlib import Path
from carta_envoy.launchers import CartaLauncher
# pylint: disable=C0301, E0401, W0622, W1203
from typing import Dict
from pycapo import CapoConfig
from carta_envoy.utilities import CAPO_UI_SETTINGS_KEY
from carta_envoy.launchers import CartaLauncher
"""
Setup and Launch CARTA via Workspaces
(modified from carta-valet)
......@@ -18,11 +23,18 @@ logger.setLevel(logging.INFO)
logger.addHandler(logging.StreamHandler(sys.stdout))
def _get_settings(location: str) -> dict:
def _get_settings(location: str) -> Dict:
"""
Returns CARTA settings
:param location: directory on which CARTA is to act
:return: various CARTA settings
"""
carta_settings = CapoConfig().settings("edu.nrao.carta")
ws_carta = CapoConfig().settings("edu.nrao.workspaces.carta")
notification_url = CapoConfig().settings("edu.nrao.workspaces.NotificationSettings").serviceUrl
workflow_url = CapoConfig().settings("edu.nrao.workspaces.WorkflowSettings").serviceUrl
ui_url = CapoConfig().settings(CAPO_UI_SETTINGS_KEY).serviceUrl
return {
"timeout": carta_settings.timeoutInMinutes,
......@@ -35,10 +47,21 @@ def _get_settings(location: str) -> dict:
"useCarta": ws_carta.useCarta,
"notification_url": notification_url,
"workflow_url": workflow_url,
"ui_url": ui_url,
"single_image": False,
}
def get_carta_settings(location: str) -> Dict:
"""
Accessor for protected function
:param location: directory on which CARTA is to act
:return: various CARTA settings
"""
return _get_settings(location)
def ensure_non_relative_path(location: str) -> Path:
if location.startswith("."):
return Path(location).resolve()
......@@ -135,7 +158,7 @@ def make_arg_parser() -> argparse.ArgumentParser:
def main():
args = make_arg_parser().parse_args()
settings = _get_settings(args.directory)
settings = get_carta_settings(args.directory)
if args.notify:
settings["user_email"] = args.notify[0]
......
......@@ -208,7 +208,7 @@ class NotificationConnect:
self.logger.info("Not sending notification because no user email supplied")
return
self.logger.info("Sending session ready notification!")
self.logger.info(f"Sending session ready notification with CARTA wrapper URL {carta_url}")
requests.post(
f"{self.url}/notify/carta_ready/send",
json={
......
......@@ -9,19 +9,16 @@ from pathlib import Path
from types import FrameType
from typing import Optional
# pylint: disable=E0401, R0913, R1721, W0603, W1203
from carta_envoy.connect import ArchiveConnect, NotificationConnect, RedisConnect
# pylint: disable=R0913, R1721, W0603, W1203
from carta_envoy.utilities import CARTA_URL_REPLACE_TEXT, CARTA_HTML_FILENAME, CARTA_TEMPLATE
"""
Setup and Launch a CARTA session
"""
CARTA_PROCESS: Optional[subprocess.Popen] = None
CARTA_URL_REPLACE_TEXT = "CARTA_URL_GOES_HERE"
CARTA_HTML_TEMPLATE_FILENAME = "carta_url_template.html"
CARTA_HTML_FILENAME = "carta_url_page.html"
class CartaLauncher:
......@@ -144,10 +141,10 @@ class CartaLauncher:
self.teardown()
sys.exit(f"ERROR: Failed to launch CARTA: {err}")
else:
carta_html = self.create_frame_html(carta_url=carta_url, html_dir=file_browser_path)
self.create_frame_html(carta_url=carta_url, html_dir=file_browser_path)
# CARTA is running and accessible, so send CARTA URL to AAT system or notify user
self.notify_ready(carta_url=carta_url, carta_html=carta_html)
self.notify_ready(carta_url=carta_url, html_dir=file_browser_path)
# Activate timeout handler
signal.signal(signal.SIGALRM, self.signal)
......@@ -161,22 +158,26 @@ class CartaLauncher:
else:
self.logger.info("Running locally...")
def notify_ready(self, carta_url: str, carta_html: Path):
def notify_ready(self, carta_url: str, html_dir: Path):
"""
Sends URL notification to user and request handler
:param carta_url: URL to CARTA session
:param html_dir: location of CARTA HTML wrapper
:return:
"""
self.logger.info("SENDING URL NOTIFICATION TO: ")
if self.settings["send_ready"] == "true":
self.logger.info("User Email")
self.logger.info(f"User Email")
self.notification.send_session_ready(carta_url)
elif self.settings["send_ready"] == "false":
self.logger.info("AAT Request Handler")
self.logger.info(f"AAT Request Handler, with {carta_url}")
self.archive_connect.send_carta_url_to_rh(carta_url)
def teardown(self):
"""
Deletes instance's Redis keys from the Redis server
......@@ -202,7 +203,8 @@ class CartaLauncher:
else:
self.logger.warning("WARNING: CARTA not running.")
def create_frame_html(self, carta_url: str, html_dir: Path) -> Path:
@staticmethod
def create_frame_html(carta_url: str, html_dir: Path) -> Path:
"""
Generate the HTML page containing the CARTA URL in a frame.
......@@ -210,25 +212,8 @@ class CartaLauncher:
:param html_dir: where HTML will be written
:return: HTML file we just created
"""
template_text = """
<!DOCTYPE html>
<html>
<head title="CARTA Session in a Frame">
<h4 style="color:#006400; font-size:24px;">
Your CARTA Session</h4>
</head>
<body>
<iframe
title="CARTA"
style="position: absolute; height: 100%; border: none"
src="CARTA_URL_GOES_HERE" style="overflow:hidden;height:100%;width:100%" height="100%" width="100%">
</iframe>
</body>
</html>
"""
new_content = template_text.replace(CARTA_URL_REPLACE_TEXT, carta_url)
new_content = CARTA_TEMPLATE.replace(CARTA_URL_REPLACE_TEXT, carta_url)
html_file = html_dir / CARTA_HTML_FILENAME
html_file.write_text(new_content)
......
......@@ -4,6 +4,28 @@ general helper methods shared between envoy modules
import random
import string
CAPO_UI_SETTINGS_KEY = "edu.nrao.workspaces.UISettings"
CARTA_URL_REPLACE_TEXT = "CARTA_URL_GOES_HERE"
CARTA_HTML_FILENAME = "carta_url_page.html"
CARTA_TEMPLATE = f"""
<!DOCTYPE html>
<html>
<head title="CARTA Session in a Frame">
<h4 style="color:#006400; font-size:24px;">
Your CARTA Session</h4>
</head>
<body>
<iframe
title="CARTA"
style="position: absolute; height: 100%; border: none"
src="{CARTA_URL_REPLACE_TEXT}" style="overflow:hidden;height:100%;width:100%" height="100%" width="100%">
</iframe>
</body>
</html>
"""
def generate_random_str() -> str:
"""
......
......@@ -5,7 +5,9 @@ import argparse
from pathlib import Path
from unittest.mock import patch
import carta_envoy.carta as carta
# pylint: disable=C0116, E0401, R0201, W0613
from carta_envoy import carta
expected_settings = {
"timeout": "1",
......@@ -19,19 +21,21 @@ expected_settings = {
"notification_url": "http://notification:3458",
"workflow_url": "http://workflow:3456",
"single_image": False,
"ui_url": "http://localhost:4444/workspaces",
}
location = "/fake/location/path"
LOCATION = "/fake/location/path"
class TestCarta:
"""Tests for carta module"""
def test_get_settings(self):
settings = carta._get_settings(location)
settings = carta.get_carta_settings(LOCATION)
for key in settings.keys():
assert settings[key] == expected_settings[key]
def test_ensure_non_relative_path(self):
path = carta.ensure_non_relative_path("/fake/location/path")
path = carta.ensure_non_relative_path(LOCATION)
assert path == expected_settings["data_location"]
@patch("os.chdir")
......
"""
Tests for carta_envoy.launchers
"""
import logging
import shutil
import tempfile
from pathlib import Path
......@@ -9,8 +10,17 @@ from unittest.mock import MagicMock, patch
# pylint: disable=E0401, R0201
import pytest
from carta_envoy.utilities import (
CARTA_URL_REPLACE_TEXT,
CARTA_HTML_FILENAME,
CARTA_TEMPLATE,
)
from carta_envoy.launchers import CartaLauncher
logger = logging.getLogger("casa_envoy")
UI_URL = "http://localhost:4444/workspaces"
CARTA_URL = UI_URL + "/carta/requests/-1/html"
test_settings = {
"timeout": 1,
"carta_path": "/fake/path/to/nowhere",
......@@ -25,10 +35,14 @@ test_settings = {
"wf_request_id": -1,
}
launcher = CartaLauncher(settings=test_settings)
SUBPROCESS_COMMAND_PATCH = "carta_envoy.launchers.subprocess.Popen"
CARTA_HTML_TEST_PATH = "/lustre/aoc/cluster/pipeline/docker/workspaces/carta/requests/1/html"
CARTA_HTML_TEST_PATH = (
"/lustre/aoc/cluster/pipeline/docker/workspaces/carta/requests/-1/html"
)
FAKE_WRAPPER_URL = "http://localhost:4444/workspaces/carta/requests/-1/html"
BACK_END_PORT = 7777
FRONT_END_PORT = 6464
......@@ -162,7 +176,9 @@ class TestCartaLauncher:
carta_path = Path(tempfile.mkdtemp())
with patch("carta_envoy.launchers.CartaLauncher.create_frame_html") as mock_carta_html:
with patch(
"carta_envoy.launchers.CartaLauncher.create_frame_html"
) as mock_carta_html:
with patch(SUBPROCESS_COMMAND_PATCH):
launcher.run_carta(
path_to_carta=str(carta_path),
......@@ -170,13 +186,27 @@ class TestCartaLauncher:
file_browser_path=Path(),
front_end_port=FRONT_END_PORT,
back_end_port=BACK_END_PORT,
carta_url="carta_url",
carta_url=CARTA_URL,
)
mock_carta_html.assert_called()
shutil.rmtree(carta_path)
def test_generates_wrapper_url(self):
"""
Does the launcher generate the expected link to the CARTA URL wrapper page?
:return:
"""
expected_url = f"{UI_URL}/carta/requests/-1/html"
logger.info("Pretending to generate CARTA wrapper url...")
wrapper_url = generate_carta_frame_url(UI_URL, Path(CARTA_HTML_TEST_PATH))
assert wrapper_url == expected_url
logger.info(f"CARTA wrapper URL: {wrapper_url}")
def test_serves_carta_wrapper(self):
"""
Can we create HTML containing the CARTA URL in a frame and serve it up?
......@@ -188,31 +218,11 @@ class TestCartaLauncher:
mock_notification_connect = MagicMock()
launcher.notification = mock_notification_connect
fake_carta_url = "fake_carta_url"
fake_carta_path = "fake_carta_path"
template_content = """
<!DOCTYPE html>
<html>
<head title="CARTA Session in a Frame">
<h4 style="color:#006400; font-size:24px;">
Your CARTA Session</h4>
</head>
<body>
<iframe
title="CARTA"
style="position: absolute; height: 100%; border: none"
src="CARTA_URL_GOES_HERE" style="overflow:hidden;height:100%;width:100%" height="100%" width="100%">
</iframe>
</body>
</html>
"""
# 3. pretend to launch CARTA
with patch("pathlib.Path.write_text"):
with patch("pathlib.Path.read_text", return_value=template_content):
with patch("pathlib.Path.read_text", return_value=CARTA_TEMPLATE):
with patch("pathlib.Path.exists", return_value=True):
with patch("pathlib.Path.is_dir", return_value=True):
with patch(SUBPROCESS_COMMAND_PATCH) as mock_subprocess:
......@@ -222,7 +232,7 @@ class TestCartaLauncher:
file_browser_path=Path(CARTA_HTML_TEST_PATH),
front_end_port=FRONT_END_PORT,
back_end_port=BACK_END_PORT,
carta_url=fake_carta_url,
carta_url=CARTA_URL,
)
assert mock_subprocess.call_count == 1
mock_subprocess.assert_called_with(
......@@ -238,35 +248,18 @@ class TestCartaLauncher:
stdout=-1,
stderr=-1,
)
mock_notification_connect.send_session_ready.assert_called_with(
fake_carta_url
)
mock_notification_connect.send_session_ready.assert_called_with(CARTA_URL)
carta_html = Path(CARTA_HTML_TEST_PATH) / "carta_url_page.html"
expected_html = """
<!DOCTYPE html>
<html>
<head title="CARTA Session in a Frame">
<h4 style="color:#006400; font-size:24px;">
Your CARTA Session</h4>
</head>
<body>
<iframe
title="CARTA"
style="position: absolute; height: 100%; border: none"
src="fake_carta_url" style="overflow:hidden;height:100%;width:100%" height="100%" width="100%">
</iframe>
</body>
</html>
"""
carta_html = Path(CARTA_HTML_TEST_PATH) / CARTA_HTML_FILENAME
expected_html = CARTA_TEMPLATE.replace(CARTA_URL_REPLACE_TEXT, CARTA_URL)
with patch("pathlib.Path.is_file", return_value=True):
assert carta_html.is_file()
with patch("pathlib.Path.read_text", return_value=expected_html):
assert fake_carta_url in carta_html.read_text()
assert CARTA_URL in carta_html.read_text()
@pytest.mark.skip("works only locally -- not in the CI. retained for hysterical porpoises")
@pytest.mark.skip(
"works only locally -- not in the CI. retained for hysterical porpoises"
)
def test_serves_carta_wrapper_no_mock(self):
"""
Can we create HTML containing the CARTA URL in a frame and serve it up?
......@@ -279,9 +272,7 @@ class TestCartaLauncher:
assert html_dir.is_dir()
# 2. copy necessary files there
template_source = (
"/packages/apps/cli/executables/pexable/carta_envoy/carta_envoy/carta_url_template.html"
)
template_source = "/packages/apps/cli/executables/pexable/carta_envoy/carta_envoy/carta_url_template.html"
shutil.copy(template_source, CARTA_HTML_TEST_PATH)
test_data_dir = Path(
......@@ -324,10 +315,27 @@ class TestCartaLauncher:
stdout=-1,
stderr=-1,
)
mock_notification_connect.send_session_ready.assert_called_with(fake_carta_url)
mock_notification_connect.send_session_ready.assert_called_with(
fake_carta_url
)
carta_html = test_data_dir / "carta_url_page.html"
assert carta_html.is_file()
assert fake_carta_url in carta_html.read_text()
shutil.rmtree(html_dir)
def generate_carta_frame_url(base_url: str, carta_html_path: Path) -> str:
"""
Convert the CARTA URL into the URL of the page that contains link to CARTA session
in a frame.
:param base_url: CARTA service prefix
:param carta_html_path: location of the wrapper page
:return:
"""
wf_request_id = carta_html_path.parent.name
return f"{base_url}/carta/requests/{wf_request_id}/{carta_html_path.name}"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment