Skip to content

Commit a0a8161

Browse files
pavlo-iuriichukknakad
authored andcommitted
feature: add utils.generate_tensorboard_url function (aws#239)
Added utils.generate_tensorboard_url function to generate TensorBoard URL for given list of s3 bucket paths
1 parent 317fc0f commit a0a8161

File tree

2 files changed

+153
-0
lines changed

2 files changed

+153
-0
lines changed

src/sagemaker/utils.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,18 @@
2424
import tempfile
2525
import time
2626

27+
2728
from datetime import datetime
2829
from functools import wraps
2930
from six.moves.urllib import parse
3031

3132
import six
3233

3334
ECR_URI_PATTERN = r"^(\d+)(\.)dkr(\.)ecr(\.)(.+)(\.)(amazonaws.com|c2s.ic.gov)(/)(.*:.*)$"
35+
MAX_BUCKET_PATHS_COUNT = 5
36+
S3_PREFIX = "s3://"
37+
HTTP_PREFIX = "http://"
38+
HTTPS_PREFIX = "https://"
3439

3540

3641
# Use the base name of the image as the job name if the user doesn't give us one
@@ -270,6 +275,55 @@ def secondary_training_status_message(job_description, prev_description):
270275
return "\n".join(status_strs)
271276

272277

278+
def generate_tensorboard_url(domain, bucket_paths):
279+
"""Generate Tensorboard URL for given list of s3 buckets
280+
281+
Args:
282+
domain: JupyterLab app domain
283+
bucket_paths: List of S3 bucket paths in format `bucket/path`
284+
or a single string in the same format
285+
286+
Returns:
287+
str: Tensorboard URL
288+
289+
Raises:
290+
AttributeError if invalid inputs are passed
291+
"""
292+
293+
def trim_prefix(s, prefix):
294+
if s.startswith(prefix):
295+
return s[len(prefix) :]
296+
return s
297+
298+
def encode_s3_url(s3_url):
299+
if not s3_url:
300+
raise AttributeError("bucket_paths element should not be empty")
301+
s3_url = trim_prefix(s3_url, S3_PREFIX)
302+
return parse.quote_plus("{}{}".format(S3_PREFIX, s3_url))
303+
304+
if not isinstance(domain, six.string_types):
305+
raise AttributeError("domain parameter should be string")
306+
307+
if len(domain) == 0:
308+
raise AttributeError("domain parameter should not be empty")
309+
310+
if isinstance(bucket_paths, six.string_types):
311+
bucket_paths = [bucket_paths]
312+
elif not isinstance(bucket_paths, list):
313+
raise AttributeError("bucket paths should be a list or a string")
314+
315+
if len(bucket_paths) == 0:
316+
raise AttributeError("bucket_paths parameter should not be empty list")
317+
318+
domain = trim_prefix(domain, HTTPS_PREFIX)
319+
domain = trim_prefix(domain, HTTP_PREFIX)
320+
321+
s3_urls = map(encode_s3_url, bucket_paths)
322+
query = ",".join(s3_urls)
323+
324+
return "https://{}/tensorboard/default?s3urls={}".format(domain, query)
325+
326+
273327
def download_folder(bucket_name, prefix, target, sagemaker_session):
274328
"""Download a folder from S3 to a local path
275329

tests/unit/test_utils.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,105 @@ def test_secondary_training_status_message_prev_missing():
208208
)
209209

210210

211+
def test_generate_tensorboard_url_valid_domain_and_bucket_paths():
212+
domain = "jupyterlab.us-east-2.abcdefgh.com"
213+
bucket_paths = ["bucket1/path1", "bucket2/path2"]
214+
expected = "https://{}/tensorboard/default?{}".format(
215+
domain, "s3urls=s3%3A%2F%2Fbucket1%2Fpath1,s3%3A%2F%2Fbucket2%2Fpath2"
216+
)
217+
assert sagemaker.utils.generate_tensorboard_url(domain, bucket_paths) == expected
218+
219+
220+
def test_generate_tensorboard_url_valid_domain_and_bucket_paths_with_s3_prefixes():
221+
domain = "jupyterlab.us-east-2.abcdefgh.com"
222+
bucket_paths = ["s3://bucket1/path1", "s3://bucket2/path2"]
223+
expected = "https://{}/tensorboard/default?{}".format(
224+
domain, "s3urls=s3%3A%2F%2Fbucket1%2Fpath1,s3%3A%2F%2Fbucket2%2Fpath2"
225+
)
226+
assert sagemaker.utils.generate_tensorboard_url(domain, bucket_paths) == expected
227+
228+
229+
def test_generate_tensorboard_url_valid_domain_and_bucket_paths_single():
230+
domain = "jupyterlab.us-east-2.abcdefgh.com"
231+
bucket_paths = ["bucket1/path1"]
232+
expected = "https://{}/tensorboard/default?{}".format(
233+
domain, "s3urls=s3%3A%2F%2Fbucket1%2Fpath1"
234+
)
235+
assert sagemaker.utils.generate_tensorboard_url(domain, bucket_paths) == expected
236+
237+
238+
def test_generate_tensorboard_url_valid_domain_and_bucket_paths_string():
239+
domain = "jupyterlab.us-east-2.abcdefgh.com"
240+
bucket_paths = "bucket1/path1"
241+
expected = "https://{}/tensorboard/default?{}".format(
242+
domain, "s3urls=s3%3A%2F%2Fbucket1%2Fpath1"
243+
)
244+
assert sagemaker.utils.generate_tensorboard_url(domain, bucket_paths) == expected
245+
246+
247+
def test_generate_tensorboard_url_valid_domain_with_http_prefix_and_bucket_paths():
248+
domain = "http://jupyterlab.us-east-2.abcdefgh.com"
249+
bucket_paths = ["bucket1/path1"]
250+
expected = "https://{}/tensorboard/default?{}".format(
251+
"jupyterlab.us-east-2.abcdefgh.com", "s3urls=s3%3A%2F%2Fbucket1%2Fpath1"
252+
)
253+
assert sagemaker.utils.generate_tensorboard_url(domain, bucket_paths) == expected
254+
255+
256+
def test_generate_tensorboard_url_valid_domain_with_https_prefix_and_bucket_paths():
257+
domain = "https://jupyterlab.us-east-2.abcdefgh.com"
258+
bucket_paths = ["bucket1/path1"]
259+
expected = "https://{}/tensorboard/default?{}".format(
260+
"jupyterlab.us-east-2.abcdefgh.com", "s3urls=s3%3A%2F%2Fbucket1%2Fpath1"
261+
)
262+
assert sagemaker.utils.generate_tensorboard_url(domain, bucket_paths) == expected
263+
264+
265+
def test_generate_tensorboard_url_bucket_path_neither_string_nor_list():
266+
domain = "jupyterlab.us-east-2.abcdefgh.com"
267+
bucket_paths = None
268+
try:
269+
sagemaker.utils.generate_tensorboard_url(domain, bucket_paths)
270+
except AttributeError as error:
271+
assert str(error) == "bucket paths should be a list or a string"
272+
273+
274+
def test_generate_tensorboard_url_empty_domain():
275+
domain = ""
276+
bucket_paths = ["bucket1/path1"]
277+
try:
278+
sagemaker.utils.generate_tensorboard_url(domain, bucket_paths)
279+
except AttributeError as error:
280+
assert str(error) == "domain parameter should not be empty"
281+
282+
283+
def test_generate_tensorboard_url_empty_bucket_paths():
284+
domain = "jupyterlab.us-east-2.abcdefgh.com"
285+
bucket_paths = []
286+
try:
287+
sagemaker.utils.generate_tensorboard_url(domain, bucket_paths)
288+
except AttributeError as error:
289+
assert str(error) == "bucket_paths parameter should not be empty list"
290+
291+
292+
def test_generate_tensorboard_url_bucket_paths_with_empty_string():
293+
domain = "jupyterlab.us-east-2.abcdefgh.com"
294+
bucket_paths = [""]
295+
try:
296+
sagemaker.utils.generate_tensorboard_url(domain, bucket_paths)
297+
except AttributeError as error:
298+
assert str(error) == "bucket_paths element should not be empty"
299+
300+
301+
def test_generate_tensorboard_url_domain_non_string():
302+
domain = None
303+
bucket_paths = ["bucket1/path1"]
304+
try:
305+
sagemaker.utils.generate_tensorboard_url(domain, bucket_paths)
306+
except AttributeError as error:
307+
assert str(error) == "domain parameter should be string"
308+
309+
211310
@patch("os.makedirs")
212311
def test_download_folder(makedirs):
213312
boto_mock = Mock(name="boto_session")

0 commit comments

Comments
 (0)