Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kedro Viz Static Website hosting on Azure #1708

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
66f177d
CLI command kedro viz build added
jitu5 Jan 4, 2024
cc7c144
Lint fix
jitu5 Jan 4, 2024
a389a83
lint fix
jitu5 Jan 4, 2024
6528d36
Lint fix
jitu5 Jan 4, 2024
5a9f2e1
add mypy ignore
jitu5 Jan 4, 2024
b86b578
Missing build file added
jitu5 Jan 8, 2024
40003f8
Lint error fix
jitu5 Jan 8, 2024
0c879a8
BaseDeployer class added
jitu5 Jan 9, 2024
5145ba4
Unused code removed
jitu5 Jan 9, 2024
26f6275
Fix lint issue
jitu5 Jan 10, 2024
52827c2
azure deploy initial draft
ravi-kumar-pilla Jan 10, 2024
c7ed79c
added base_deployer
rashidakanchwala Jan 10, 2024
11ac321
latest
rashidakanchwala Jan 10, 2024
42af3af
add deployer factory
rashidakanchwala Jan 11, 2024
aff36ec
partial working draft
ravi-kumar-pilla Jan 12, 2024
7ad0905
Test and comments of deployers updated
jitu5 Jan 12, 2024
0ff98a2
test draft
ravi-kumar-pilla Jan 15, 2024
2a4e00f
fix lint
ravi-kumar-pilla Jan 15, 2024
ecb6a05
remove circular dependency
ravi-kumar-pilla Jan 15, 2024
39418aa
merge build
ravi-kumar-pilla Jan 15, 2024
b18d547
merge main and modify azure deployer
ravi-kumar-pilla Jan 17, 2024
8b504fb
fix lint
ravi-kumar-pilla Jan 17, 2024
9d683aa
revert back consent
ravi-kumar-pilla Jan 17, 2024
2655d1a
minor updates
ravi-kumar-pilla Jan 18, 2024
2ac04fd
update pytests
ravi-kumar-pilla Jan 19, 2024
c258ec1
add pytest for azure shareableviz
ravi-kumar-pilla Jan 19, 2024
44241ca
refactor and add timeout
ravi-kumar-pilla Jan 22, 2024
40df81e
merge main
ravi-kumar-pilla Jan 23, 2024
c9e2436
refactor cli
ravi-kumar-pilla Jan 23, 2024
9c31fc4
update pytest
ravi-kumar-pilla Jan 23, 2024
e05630b
add release note
ravi-kumar-pilla Jan 23, 2024
e1016e2
fix flaky test
ravi-kumar-pilla Jan 23, 2024
2868fa3
fix PR comments and flaky test
ravi-kumar-pilla Jan 24, 2024
c934570
testing flaky c
ravi-kumar-pilla Jan 24, 2024
6572ce0
remove flaky test
ravi-kumar-pilla Jan 24, 2024
3758a53
merge main
ravi-kumar-pilla Jan 24, 2024
09e1c4c
resolve conflicts
ravi-kumar-pilla Jan 24, 2024
3b34d8a
fix PR comments
ravi-kumar-pilla Jan 25, 2024
9b9478a
Merge branch 'main' of https://github.com/kedro-org/kedro-viz into fe…
ravi-kumar-pilla Feb 6, 2024
bce7258
merge extended support
ravi-kumar-pilla Feb 8, 2024
c27f419
add back cypress flaky test
ravi-kumar-pilla Feb 8, 2024
496c2c4
remove cypress flaky test
ravi-kumar-pilla Feb 8, 2024
bb2e6a9
remove duplicate pytest parameter
ravi-kumar-pilla Feb 8, 2024
cd05c91
remove fsspec upper bound
ravi-kumar-pilla Feb 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ Please follow the established format:
- Use present tense (e.g. 'Add new feature')
- Include the ID number for the related PR (or PRs) in parentheses
-->
# Upcoming Release

## Major features and improvements

- Kedro Viz Static Website hosting on Azure. (#1708)
ravi-kumar-pilla marked this conversation as resolved.
Show resolved Hide resolved

# Upcoming release

Expand Down
3 changes: 0 additions & 3 deletions cypress/tests/ui/flowchart/shareable-urls.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,6 @@ describe('Shareable URLs', () => {
it('verifies that error message appears with wrong inputs on publish button click #TC-59', () => {
const bucketName = 'myBucketName';
const primaryButtonNodeText = 'Publish';
const errorButtonNodeText = 'Go back';

// Action
cy.get('.pipeline-menu-button--deploy').click();
cy.get('.shareable-url-modal [data-test=kedro-pipeline-selector]').click();
Expand All @@ -135,7 +133,6 @@ describe('Shareable URLs', () => {
cy.get('.shareable-url-modal .modal__wrapper').contains(
'Something went wrong. Please try again later.'
);
cy.get('.shareable-url-modal__error button').contains(errorButtonNodeText);
});

it('verifies that AWS link is generated with correct inputs on publish button click #TC-60', () => {
Expand Down
2 changes: 1 addition & 1 deletion package/kedro_viz/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@

VIZ_DEPLOY_TIME_LIMIT = 300

SHAREABLEVIZ_SUPPORTED_PLATFORMS = ["aws"]
SHAREABLEVIZ_SUPPORTED_PLATFORMS = ["aws", "azure"]
84 changes: 84 additions & 0 deletions package/kedro_viz/integrations/deployment/azure_deployer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
"""`kedro_viz.integrations.deployment.azure_deployer` defines
deployment class for Azure Blob Storage"""
import glob
import logging
import mimetypes
from pathlib import Path

from kedro_viz.integrations.deployment.base_deployer import BaseDeployer

try:
from azure.storage.blob import ContentSettings
except ImportError: # pragma: no cover
pass

import fsspec

from kedro_viz import __version__

_AZ_PROTOCOL = "abfs"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we reused logic from kedro? I don't think abfs is the only protocols supported by Azure. See https://github.com/fsspec/adlfs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we are using Gen2 filesystem to build the remote path (we will mention this in our docs that azure deployment is supported with gen2 filesystem). Could you please point me to what reuse logic from kedro you are referring to ? Thank you

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/kedro-org/kedro/blob/80ad182a220d691c77efc05cc4e2b36dcecb6ac7/kedro/io/core.py#L711
95% identical to fsspec to handle some extra edge cases. If this only support abfs it is fine too, but worth mentioning in the docs.

logger = logging.getLogger(__name__)


class AzureDeployer(BaseDeployer):
"""A class to handle the deployment of Kedro-viz to AzureBlobStorage.

Attributes:
_endpoint (str): Azure endpoint of the hosted site.
_bucket_name (str): Name of the AzureBlobStorage account.
_path (str): Container path for the AzureBlobStorage account.
_fs (fsspec.filesystem): Filesystem for Azure protocol.
"""

def __init__(self, endpoint, bucket_name):
"""Initialize AzureBlobStorage with endpoint and bucket name.

Args:
endpoint (str): Azure endpoint of the hosted site.
bucket_name (str): Name of the AzureBlobStorage account.
"""
super().__init__()
self._endpoint = endpoint
self._bucket_name = bucket_name
self._path = f"{_AZ_PROTOCOL}://$web"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is $web?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$web is the default container in AzureBlobStorage when someone enables static website hosting. We will mention this in the docs that the users need to have this container to use static hosting.

self._fs = fsspec.filesystem(_AZ_PROTOCOL, **{"account_name": bucket_name})

def _write_heap_injected_index(self, html_content):
ravi-kumar-pilla marked this conversation as resolved.
Show resolved Hide resolved
self._fs.write_bytes(
path=f"{self._path}/index.html",
value=html_content,
overwrite=True,
**{"content_settings": ContentSettings(content_type="text/html")},
)

def _upload_static_files(self, html_dir: Path):
logger.debug("Uploading static html files to %s.", self._path)
try:
file_list = glob.glob(f"{str(html_dir)}/**/*", recursive=True)

for local_file_path in file_list:
content_type, _ = mimetypes.guess_type(local_file_path)

# ignore directories
if content_type is None: # pragma: no cover
continue

relative_path = local_file_path[len(str(html_dir)) + 1 :]
remote_file_path = f"{self._path}/{relative_path}"

# Read the contents of the local file
with open(local_file_path, "rb") as file:
content = file.read()

self._fs.write_bytes(
path=remote_file_path,
value=content,
overwrite=True,
**{"content_settings": ContentSettings(content_type=content_type)},
)

self._ingest_heap_analytics()

except Exception as exc: # pragma: no cover
logger.exception("Upload failed: %s ", exc)
raise exc
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"""`kedro_viz.integrations.deployment.deployer_factory` creates
Kedro-viz deployer class instances"""

from kedro_viz.constants import SHAREABLEVIZ_SUPPORTED_PLATFORMS
from kedro_viz.integrations.deployment.aws_deployer import AWSDeployer
from kedro_viz.integrations.deployment.azure_deployer import AzureDeployer
from kedro_viz.integrations.deployment.local_deployer import LocalDeployer


Expand All @@ -13,6 +15,11 @@ def create_deployer(platform, endpoint=None, bucket_name=None):
"""Instantiate Kedro-viz deployer classes"""
if platform.lower() == "aws":
return AWSDeployer(endpoint, bucket_name)
if platform.lower() == "azure":
return AzureDeployer(endpoint, bucket_name)
if platform.lower() == "local":
return LocalDeployer()
raise ValueError(f"Invalid platform '{platform}' specified")
raise ValueError(
f"Invalid platform '{platform}' specified. \n"
f"Kedro-Viz supports the following platforms - {*SHAREABLEVIZ_SUPPORTED_PLATFORMS,}"
)
3 changes: 1 addition & 2 deletions package/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ kedro>=0.18.0
ipython>=7.0.0, <9.0
fastapi>=0.73.0,<0.200.0
pydantic<2
fsspec>=2021.4, <2024.1
s3fs>=2021.4, <2024.1
fsspec>=2021.4
aiofiles>=22.1.0
uvicorn[standard]~=0.25.0
watchgod~=0.8.2
Expand Down
2 changes: 2 additions & 0 deletions package/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,7 @@
"sphinx_rtd_theme==1.3.0",
"myst-parser>=1.0,<2.1",
],
"aws": ["s3fs>=2021.4"],
"azure": ["adlfs>=2021.4"],
},
)
4 changes: 4 additions & 0 deletions package/test_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,7 @@ types-PyYAML==0.1.5
types-requests==0.1.8
types-toml==0.1.1
types-ujson==0.1.0

#shareableviz
s3fs>=2021.4, <2024.1
adlfs>=2021.4, <2024.1
79 changes: 79 additions & 0 deletions package/tests/test_integrations/test_azure_deployer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import pytest

from kedro_viz import __version__
from kedro_viz.integrations.deployment.azure_deployer import AzureDeployer

try:
from azure.storage.blob import ContentSettings
except ImportError: # pragma: no cover
pass


# Test the AzureDeployer class
@pytest.fixture
def endpoint():
return "https://test-bucket.z13.web.core.windows.net/"


@pytest.fixture
def bucket_name():
return "test-bucket"


@pytest.fixture
def mock_file_system(mocker):
yield mocker.patch("fsspec.filesystem")


class TestAzureDeployer:
def test_deploy(self, endpoint, bucket_name, mocker):
deployer = AzureDeployer(endpoint, bucket_name)

mocker.patch.object(deployer, "_upload_api_responses")
mocker.patch.object(deployer, "_upload_static_files")
mocker.patch.object(deployer, "_upload_deploy_viz_metadata_file")

deployer.deploy()

deployer._upload_api_responses.assert_called_once()
deployer._upload_static_files.assert_called_once()
deployer._upload_deploy_viz_metadata_file.assert_called_once()

def test_write_heap_injected_index(self, endpoint, bucket_name, mock_file_system):
mock_html_content = "<html>Mocked Content</html>"
deployer = AzureDeployer(endpoint, bucket_name)

deployer._write_heap_injected_index(mock_html_content)

# Assertions
deployer._fs.write_bytes.assert_called_once_with(
path=f"{deployer._path}/index.html",
value=mock_html_content,
overwrite=True,
**{"content_settings": ContentSettings(content_type="text/html")},
)

def test_upload_static_files(
self, endpoint, bucket_name, tmp_path, mocker, mock_file_system
):
deployer = AzureDeployer(endpoint, bucket_name)
mock_ingest_heap_analytics = mocker.patch.object(
deployer, "_ingest_heap_analytics"
)
mock_html_content = "<html><body>Test Content</body></html>"

# Create a temporary HTML file with some test content
temp_file_path = tmp_path / "test_file.html"
with open(temp_file_path, "w", encoding="utf-8") as temp_file:
temp_file.write(mock_html_content)

with mocker.patch("mimetypes.guess_type", return_value=("text/html", None)):
deployer._upload_static_files(tmp_path)
deployer._fs.write_bytes.assert_called_once_with(
path="abfs://$web/test_file.html",
value=mock_html_content.encode("utf-8"),
overwrite=True,
**{"content_settings": ContentSettings(content_type="text/html")},
)

mock_ingest_heap_analytics.assert_called_once()
15 changes: 13 additions & 2 deletions package/tests/test_integrations/test_deployer_factory.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import re

import pytest

from kedro_viz.constants import SHAREABLEVIZ_SUPPORTED_PLATFORMS
from kedro_viz.integrations.deployment.aws_deployer import AWSDeployer
from kedro_viz.integrations.deployment.azure_deployer import AzureDeployer
from kedro_viz.integrations.deployment.deployer_factory import DeployerFactory
from kedro_viz.integrations.deployment.local_deployer import LocalDeployer


@pytest.mark.parametrize(
"platform, endpoint, bucket_name, deployer_class",
[("aws", "http://mocked-url.com", "s3://shareableviz", AWSDeployer)],
[
("aws", "http://mocked-url.com", "s3://shareableviz", AWSDeployer),
("azure", "http://mocked-url.com", "abfs://shareableviz", AzureDeployer),
],
)
def test_create_deployer(platform, endpoint, bucket_name, deployer_class):
deployer = DeployerFactory.create_deployer(platform, endpoint, bucket_name)
Expand All @@ -23,6 +30,10 @@ def test_create_deployer_local():

def test_create_deployer_invalid_platform():
with pytest.raises(
ValueError, match="Invalid platform 'invalid_platform' specified"
ValueError,
match=re.escape(
f"Invalid platform 'invalid_platform' specified. \n"
f"Kedro-Viz supports the following platforms - {*SHAREABLEVIZ_SUPPORTED_PLATFORMS,}"
),
):
DeployerFactory.create_deployer("invalid_platform")
28 changes: 21 additions & 7 deletions package/tests/test_launchers/test_cli.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,23 @@ def test_viz_command_group(mocker, mock_click_echo):
@pytest.mark.parametrize(
"command_options, deployer_args",
[
(
[
"viz",
"deploy",
"--platform",
"azure",
"--endpoint",
"https://example-bucket.web.core.windows.net",
"--bucket-name",
"example-bucket",
],
{
"platform": "azure",
"endpoint": "https://example-bucket.web.core.windows.net",
"bucket_name": "example-bucket",
},
),
(
[
"viz",
Expand Down Expand Up @@ -409,19 +426,21 @@ def test_successful_build_with_existing_static_files(mocker):
@pytest.mark.parametrize(
"platform, endpoint, bucket_name, process_completed_value",
[
("azure", "https://example-bucket.web.core.windows.net", "example-bucket", 1),
(
"aws",
"http://example-bucket.s3-website.us-east-2.amazonaws.com/",
"example-bucket",
1,
),
("local", None, None, 1),
("azure", "https://example-bucket.web.core.windows.net", "example-bucket", 0),
(
"aws",
"http://example-bucket.s3-website.us-east-2.amazonaws.com/",
"example-bucket",
0,
),
("local", None, None, 1),
("local", None, None, 0),
],
)
Expand Down Expand Up @@ -484,17 +503,12 @@ def test_create_shareableviz_process(
@pytest.mark.parametrize(
"platform, endpoint, bucket_name",
[
("azure", "https://example-bucket.web.core.windows.net", "example-bucket"),
(
"aws",
"http://example-bucket.s3-website.us-east-2.amazonaws.com/",
"example-bucket",
),
(
"aws",
"http://example-bucket.s3-website.us-east-2.amazonaws.com/",
"example-bucket",
),
("local", None, None),
("local", None, None),
],
)
Expand Down