-
Notifications
You must be signed in to change notification settings - Fork 65
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
[RFR][SVCS-336]MFR issue: cannot render (videos, PDFs, or PDBs) locally when dockerized #259
[RFR][SVCS-336]MFR issue: cannot render (videos, PDFs, or PDBs) locally when dockerized #259
Conversation
mfr/extensions/video/render.py
Outdated
@@ -13,7 +14,14 @@ class VideoRenderer(extension.BaseRenderer): | |||
]).get_template('viewer.mako') | |||
|
|||
def render(self): | |||
return self.TEMPLATE.render(base=self.assets_url, url=self.url) | |||
self_url = urlparse(self.url) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This problem could more generally also affect other renderers that directly serve the file content, such as PDB files. Might be worth checking for similar edge case bugs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. Yep, already looking at updating pdf renderer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, so it also affects a third filetype? Good find. Hope we can DRY it a bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- works as expected with my local env
- looks good except a few style improvements
I also have a general question. Why this only fails for the three types but not others. Is it worth it to make this generalized for all instead of by specific extension?
Back to Add'l Dev 🎆
mfr/extensions/video/render.py
Outdated
@@ -13,7 +15,11 @@ class VideoRenderer(extension.BaseRenderer): | |||
]).get_template('viewer.mako') | |||
|
|||
def render(self): | |||
return self.TEMPLATE.render(base=self.assets_url, url=self.url) | |||
self_url = urlparse(self.metadata.download_url) | |||
# Catch for local developement. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: "development" (3 occurrences)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
complete
mfr/extensions/settings.py
Outdated
@@ -0,0 +1,6 @@ | |||
from mfr import settings | |||
|
|||
config = settings.child('EXTENTION_CONFIG') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: 'EXTENSTION_CONFIG'
(1 occurence)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
complete
mfr/extensions/utils.py
Outdated
|
||
def development_export_url(urlparsed_url): | ||
query_dict = parse_qs(urlparsed_url.query, keep_blank_values=True) | ||
query_dict.pop('mode', None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we pop mode
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's been quite a while since I worked on this and don't recall off the top of my head. In brief testing I was not able to come up with a good answer other then that it isn't needed. I can remove the pop, but it's very possible I had a good reason that I no longer recall.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This only affects local development. We can keep it there with the TODO
note for you to recall the reason. :D
mfr/extensions/utils.py
Outdated
urlparsed_url = urlparsed_url._replace(query=urlencode(query_dict, doseq=True)) | ||
urlparsed_url = urlparsed_url._replace(netloc="{}:{}".format(settings.LOCAL_HOST, | ||
urlparsed_url.port)) | ||
return urlparsed_url |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style suggestions:
- indent by 4
- 2 empty lines after imports
- naming
- docs/comments
Here is my example:
from urllib.parse import parse_qs, urlencode
from mfr.extensions import settings
def get_local_export_url(parsed_url):
"""
Replace the docker domain with localhost (local development only).
:param parsed_url: the default export URL which is parsed by `urllib.parse.urlparse`
:return: the tweaked export URL for local development
"""
query_dict = parse_qs(parsed_url.query, keep_blank_values=True)
# TODO: why do we pop `mode`?
query_dict.pop('mode', None)
parsed_url = parsed_url._replace(query=urlencode(query_dict, doseq=True))
parsed_url = parsed_url._replace(
netloc="{}:{}".format(settings.LOCAL_HOST, parsed_url.port)
)
return parsed_url
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Complete
mfr/extensions/pdb/render.py
Outdated
@@ -16,9 +19,14 @@ class PdbRenderer(extension.BaseRenderer): | |||
]).get_template('viewer.mako') | |||
|
|||
def render(self): | |||
self_url = urlparse(self.metadata.download_url) | |||
# Catch for local developement. | |||
if self_url.hostname == ext_settings.DOCKER_LOCAL_HOST: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we make this equation check a flag instead? For example, in mfr/extenstion/settings
:
# only set to `true` for local development
DOCKER_LOCAL_HOST = config.get('DOCKER_LOCAL_HOST', false)
And we check:
if ext_settings.DOCKER_LOCAL_HOST:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see what you mean. However, I like the explicit DOCKER_LOCAL_HOST setting. I think it's less confusing. When someone sees the 192.168.168.167 address, they know just what we're talking about. Also, I like the explicitness of specifying the IP we will change, and no other. I'll split the difference with you and add another config LOCAL_DEVELOPMENT.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. What you have looks good! 👍
And probably no need for the flag
I suggested. It leads to confusion and developers need to update local configuration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DOH! too late already did it, though I can undo it if you don't like it. You will now need to either add LOCAL_DEVELOPMENT=1 to your ~.cos file for MFR or...
add this line to your OSF.io .docker-compose.mfr.env...
EXTENSION_CONFIG_LOCAL_DEVELOPMENT=1
I'll make a coresponding OSF.io PR when this PR is finalized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried not to disturb the PR too much. It is better to have both in place (and it is) to make sure this only works for local docker environment.
mfr/extensions/pdf/render.py
Outdated
return self.TEMPLATE.render(base=self.assets_url, url=self.metadata.download_url) | ||
self_url = urlparse(self.metadata.download_url) | ||
# Catch for local developement. | ||
if self_url.hostname == settings.DOCKER_LOCAL_HOST: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as mentioned above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as mentioned above.
RE: general question In local development, those MFR extensions that create links to WB files in TEMPLATE, must use localhost instead of 192.168.168.167. This is because the request will come from the browser instead of the MFR docker instance. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good 🔥 . Move to PCR (Passed Code Review).
@felliott Please double check before moving to RTM.
@TomBaxter Please add a dependency for the OSF ticket you mentioned.
related PR created. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some refactorings needed, and a few questions about the implementation of the url munger.
mfr/extensions/pdb/render.py
Outdated
@@ -16,9 +19,14 @@ class PdbRenderer(extension.BaseRenderer): | |||
]).get_template('viewer.mako') | |||
|
|||
def render(self): | |||
self_url = urlparse(self.metadata.download_url) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code here and in the render
methods for the pdf and video exporters are almost identical. Could you move this into a decorator/utility method/base class/whatever to avoid the code duplication?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Completed.
mfr/extensions/utils.py
Outdated
def get_local_export_url(urlparsed_url): | ||
""" | ||
Replace the docker domain with localhost (for use in local development). | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: show a simple example, like:
http://localhost:7777/foo/bar => http://192.168.168.167:7777/foo/bar
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
completed.
mfr/extensions/utils.py
Outdated
""" | ||
|
||
query_dict = parse_qs(urlparsed_url.query, keep_blank_values=True) | ||
# TODO: why do we pop `mode`? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
??? Did this come from somewhere else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was a question from @cslzchen that I couldn't remember the answer to.
mfr/extensions/utils.py
Outdated
""" | ||
Replace the docker domain with localhost (for use in local development). | ||
|
||
:param parsed_url: the default export URL which is parsed by `urllib.parse.urlparse` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your docstring is out-of-date.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Completed, I think?
mfr/extensions/utils.py
Outdated
urlparsed_url = urlparsed_url._replace(query=urlencode(query_dict, doseq=True)) | ||
urlparsed_url = urlparsed_url._replace( | ||
netloc="{}:{}".format(settings.LOCAL_HOST, urlparsed_url.port) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not familiar with the _replace
method. What does it do, and why are you using a private method? Is there another way to do this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sure this can be done many other ways, but I was trying to do the least amount of converting. This way I am just manipulating the ParseResult, a namedtuple subclass. Dropping the "mode" query parameter and switching the netloc.
https://docs.python.org/3.6/library/collections.html#collections.somenamedtuple._replace
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very close, almost all nitpicks. Two minor issues:
- The name of the decorator should be more descriptive.
- The popping of the
mode
parameter should either be explained or removed. Please try it without and see what breaks.
Cheers,
@felliott
mfr/extensions/pdb/render.py
Outdated
@@ -5,6 +5,7 @@ | |||
from mako.lookup import TemplateLookup | |||
|
|||
from mfr.core import extension | |||
from mfr.extensions.utils import download_from_template |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: follow PEP8 import order, this should go after mfr.extensions.pdb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Completed.
mfr/extensions/utils.py
Outdated
@@ -0,0 +1,22 @@ | |||
from urllib.parse import urlparse, parse_qs, urlencode | |||
from mfr.extensions import settings |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: leave one space between import stanzas
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Completed.
# TEMPLATE to render, when using local docker development environment. Call will be coming from | ||
# browser instead of docker container. | ||
|
||
LOCAL_DEVELOPMENT = config.get_bool('LOCAL_DEVELOPMENT', 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: 0
==> False
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can make this change and it will work fine. The reason I picked 0 instead is because the corresponding osf.io .docker-compose.mfr.env file can't set LOCAL_DEVELOPMENT to True
, must be a string, therefor I chose "1" as that is covered by setting.get_bool. You still want I should change it to False
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything else looks good, so I'll take care of it. But generally the settings should follow the idiom they're defined in. This is a python config file, so True
and False
are appropriate. The .docker-compose.*.env
files are env files, so 1
and 0
are appropriate for them.
mfr/extensions/utils.py
Outdated
from mfr.extensions import settings | ||
|
||
|
||
def download_from_template(func): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The decorator looks good to me, but I'd like to bikeshed the name, download_from_template
is a little nonspecific. Maybe munge_url_for_localdev
or something like that? Or even munge_download_url
if this might end up doing more than just rewriting the localdev download url. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. Completed.
mfr/extensions/utils.py
Outdated
download_url = urlparse(self.metadata.download_url) | ||
if (settings.LOCAL_DEVELOPMENT and download_url.hostname == settings.DOCKER_LOCAL_HOST): | ||
query_dict = parse_qs(download_url.query, keep_blank_values=True) | ||
query_dict.pop('mode', None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove this and see what breaks. If it's needed, add it back with a comment explaining why. If not, leave it out. If we can't figure out why we need it, we should leave it out until we know why.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wasn't able to reproduce an issue, so removing. Completed.
mfr/extensions/utils.py
Outdated
query_dict = parse_qs(download_url.query, keep_blank_values=True) | ||
query_dict.pop('mode', None) | ||
download_url = download_url._replace(query=urlencode(query_dict, doseq=True)) | ||
download_url = download_url._replace( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First, my bad: I thought the leading underscore meant this was a private method. Clearly I need to learn more python.
Nitpick: you can modify both fields of the named tuple in a single _replace
command.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Completed.
mfr/extensions/utils.py
Outdated
query_dict.pop('mode', None) | ||
download_url = download_url._replace(query=urlencode(query_dict, doseq=True)) | ||
download_url = download_url._replace( | ||
netloc="{}:{}".format(settings.LOCAL_HOST, download_url.port) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super-nitpick: please use single quotes for literals.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Completed.
mfr/extensions/video/render.py
Outdated
@@ -2,6 +2,7 @@ | |||
|
|||
from mako.lookup import TemplateLookup | |||
|
|||
from mfr.extensions.utils import download_from_template | |||
from mfr.core import extension |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: import order
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
completed.
mfr/extensions/utils.py
Outdated
def download_from_template(func): | ||
""" | ||
Check if LOCAL_DEVELOPMENT and if so | ||
Replace the docker domain with localhost (for use in local development). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copyedit: "If MFR is being run in a local development environment (i.e. LOCAL_DEVELOPMENT is True), we need to replace the external host (the one the user provides, default: "localhost") with the internal host (the one the backend services communicate on, default: 192.168.168.167)."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Completed. Note: I had to switch the to/from. I probably confused you with the bad example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CRR completed.
# TEMPLATE to render, when using local docker development environment. Call will be coming from | ||
# browser instead of docker container. | ||
|
||
LOCAL_DEVELOPMENT = config.get_bool('LOCAL_DEVELOPMENT', 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can make this change and it will work fine. The reason I picked 0 instead is because the corresponding osf.io .docker-compose.mfr.env file can't set LOCAL_DEVELOPMENT to True
, must be a string, therefor I chose "1" as that is covered by setting.get_bool. You still want I should change it to False
?
mfr/extensions/pdb/render.py
Outdated
@@ -5,6 +5,7 @@ | |||
from mako.lookup import TemplateLookup | |||
|
|||
from mfr.core import extension | |||
from mfr.extensions.utils import download_from_template |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Completed.
mfr/extensions/utils.py
Outdated
@@ -0,0 +1,22 @@ | |||
from urllib.parse import urlparse, parse_qs, urlencode | |||
from mfr.extensions import settings |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Completed.
mfr/extensions/utils.py
Outdated
from mfr.extensions import settings | ||
|
||
|
||
def download_from_template(func): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. Completed.
mfr/extensions/utils.py
Outdated
def download_from_template(func): | ||
""" | ||
Check if LOCAL_DEVELOPMENT and if so | ||
Replace the docker domain with localhost (for use in local development). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Completed. Note: I had to switch the to/from. I probably confused you with the bad example.
mfr/extensions/utils.py
Outdated
query_dict = parse_qs(download_url.query, keep_blank_values=True) | ||
query_dict.pop('mode', None) | ||
download_url = download_url._replace(query=urlencode(query_dict, doseq=True)) | ||
download_url = download_url._replace( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Completed.
mfr/extensions/utils.py
Outdated
query_dict.pop('mode', None) | ||
download_url = download_url._replace(query=urlencode(query_dict, doseq=True)) | ||
download_url = download_url._replace( | ||
netloc="{}:{}".format(settings.LOCAL_HOST, download_url.port) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Completed.
mfr/extensions/video/render.py
Outdated
@@ -2,6 +2,7 @@ | |||
|
|||
from mako.lookup import TemplateLookup | |||
|
|||
from mfr.extensions.utils import download_from_template | |||
from mfr.core import extension |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
completed.
mfr/extensions/utils.py
Outdated
download_url = urlparse(self.metadata.download_url) | ||
if (settings.LOCAL_DEVELOPMENT and download_url.hostname == settings.DOCKER_LOCAL_HOST): | ||
query_dict = parse_qs(download_url.query, keep_blank_values=True) | ||
query_dict.pop('mode', None) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wasn't able to reproduce an issue, so removing. Completed.
Add EXTENSION settings for.. LOCAL_HOST, DOCKER_LOCAL_HOST, and LOCAL_DEVELOPMENT Add mfr.extension.utils.download_from_template decorator to... 1. Switch DOCKER_LOCAL_HOST to LOCAL_HOST 2. Remove "mode" parameter For renderers pdb, pdf, and video: Add download_from_template decorator to render method Fix test for video renderer In local development, those MFR extensions that create links to WB files in TEMPLATES, must use localhost instead of 192.168.168.167. This is because the request will come from the browser instead of the MFR docker instance.
[#SVCS-336]
A!W!E!S!O!M!E! Moving to RTM. |
Purpose:
In local development, those MFR extensions that create javascript links to WB files, must use localhost instead of 192.168.168.167. This is because the request will come from the browser instead of the MFR docker instance.
Add catch for rendering in local development
Changes:
Add EXTENSION settings for LOCAL_HOST and DOCKER_LOCAL_HOST
Add mfr.extension.utils.get_local_export_url to ...
1. Switch DOCKER_LOCAL_HOST to LOCAL_HOST
2. Remove "mode" parameter
For renderers pdb, pdf, and video:
Check for DOCKER_LOCAL_HOST and call mfr.extension.utils.get_local_export_url
Side effects
None
Important
There is a related PR That adds EXTENSION_CONFIG_LOCAL_DEVELOPMENT=1 to .docker-compose.mfr.env . Would be nice if the were merged together, though nothing bad would happen if they weren't.
[#SVCS-336]
EXTENSION_CONFIG_LOCAL_DEVELOPMENT=1