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

ng: support path_prefix at router level #4423

Merged
merged 12 commits into from
Jan 13, 2021
Merged

Conversation

stephanwlee
Copy link
Contributor

@stephanwlee stephanwlee commented Dec 4, 2020

Problem:
TensorBoard has a flag, path_prefix, which serves frontend and the data
endpoints at your specified, if the flag is passed, pathname. While
exact use case slips my mind, it is certainly used by dependents. Now,
the problem is that new Angular based TensorBoard has a custom router
for in-app navigation (we may add new routes for experiment list and
others) it is unaware of the path prefix.

Router works by matching set of predefined routes to current pathname
and finds a match. However, the configuration is unaware (rightfully) of
the path prefix and it results in zero match, which in turn cause
navigation to the default path, "/". This totally breaks the assumption
of the path prefix as all the request from the frontend will be sent to
"/data/..." as opposed to "[path_prefix]/data/..."

Remediation:
Previously, there was no reason to propagate the prefix information to
the frontend. Application only did hash changes and never required a
navigation. With this change, we now:

  • server side render meta tag that depicts the relative path to the root of the
    application
  • bootstrap Angular app from the relative root path and uses that information
    for matching routes and navigation

Tests:

  • manually tested with/without path_prefix
  • tested dynamic plugin with/without path_prefix (projector)

@google-cla google-cla bot added the cla: yes label Dec 4, 2020
@stephanwlee stephanwlee requested a review from psybuzz December 4, 2020 06:04
@wchargin
Copy link
Contributor

wchargin commented Dec 4, 2020

A passing thought and question…

One technique that I’ve used in the past is to simply inspect
window.location.pathname to get the path prefix on the frontend when
the app starts, before any navigation. This is especially simple for
TensorBoard since we’re a single-page app, but it’s also easy to extend
to multi-page apps, even with client side routing. Then you don’t need
any special backend logic, and in fact this works perfectly with a
static server even when you don’t know the path prefix upfront.

It seems to me like that would be a bit simpler. Is there a reason that
we prefer introducing a new /data/config route?

Or, in other words—

Previously, there was no reason to propagate the prefix information to
the frontend. Application only did hash changes and never required a
navigation.

—why does this have to change?

@stephanwlee
Copy link
Contributor Author

One technique that I’ve used in the past is to simply inspect
window.location.pathname to get the path prefix on the frontend when
the app starts, before any navigation

There is no guarantee that application will only have "/" route and it is legal for users to start the app at "/foo/bar/experiment/123/"

@wchargin
Copy link
Contributor

wchargin commented Dec 4, 2020

There is no guarantee that application will only have "/" route and it is legal for users to start the app at "/foo/bar/experiment/123/"

If I launch with --path_prefix /foo/bar at this patch and navigate to
/foo/bar/experiment/123/, it redirects me to just /foo/bar/, and
makes initial requests to /foo/bar/experiment/123/data/runs but then
later makes requests to just /foo/bar/data/runs. Is that working as
intended? (Like, it doesn’t matter for the OSS data providers, but it
doesn’t seem right to me.)

Anwyay, what I’ve typically done for this is to include in each page’s
static HTML a relative path reference from that page to the root of the
application:

<!-- /index.html -->
<body initial-root=".">
  <h1>Home</h1>
</body>
<!-- /about/roadmap/index.html -->
<body initial-root="../..">
  <h1>Our roadmap</h1>
</body>

Then, when index.js starts, it grabs body.dataset.initialRoot and
records normalize(`${pathname}/${initialRoot}/`) as the path prefix,
which works independent of the pathname, can be hosted statically, and
doesn’t add another RPC to the critical path.

But I recognize that this is probably a bit harder for us since we don’t
really do proper server side rendering, so okay with me to proceed with
your approach.

Copy link
Contributor

@psybuzz psybuzz left a comment

Choose a reason for hiding this comment

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

This PR seems to break Time Series in standalone TB :/

The app 404s on a request to https://.../experiment/defaultExperimentId/data/plugin/timeseries/tags because metrics_data_source uses a url starting with '/'. I wonder, should we patch TBHttpClient to add the path_prefix whenever its methods are called with an absolute-looking url?

Re: stamping the initial HTML with path_prefix (or the relative path to it, as per wchargin)
I don't have a strong preference between that approach and the current PR. If we end up needing to switch to the stamped HTML approach for performance reasons later, most of this PR's frontend code doesn't have to change.

@@ -44,6 +45,7 @@ import {Navigation, Route} from '../types';
/** @typehack */ import * as _typeHackNgrxEffects from '@ngrx/effects/effects';
/** @typehack */ import * as _typeHackModels from '@ngrx/store/src/models';
/** @typehack */ import * as _typeHackStore from '@ngrx/store';
import {getPathPrefixConfig} from '../../core/store';
Copy link
Contributor

Choose a reason for hiding this comment

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

Just ran a test sync and it looks like we'll need an explicit dep to get it to build.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for doing that. I was going to run the sync test after LGTM but you beat me to it!

"path_prefix": "/foo/bar"
"window_title": "Custom Name",
}

## `data/environment`

Returns environment in which the TensorBoard app is running.
Copy link
Contributor

Choose a reason for hiding this comment

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

Now that we've copied the exp-agnostic fields from /data/environment into /data/config, the doc section for data/environment is stale.

In this PR, anything consuming window_title is either

  • getting it properly from the exp-agnostic endpoint /data/config
  • or is stuck on the legacy /data/environment, and needs to migrate so that we can make /data/environment exp-specific

When imagining the future TB that supports multiple experiments, I would assume the more appropriate place to get the data_location (and other experiment-specific info) is not /data/env or /data/config, but from one of the experiment-specific endpoints that already exist (/data/experiments or /data/experiment_runs or /data/runs). In this scenario, we don't need both /data/config AND /data/env, but only 1 of them to serve exp-agnostic prefix_path, window_title, so we may consider dropping one of them.

Firstly, do you agree that we'd eventually need [/data/config OR /data/env], but not both?

If so, it sounds like the progression after this PR would be to:

  • migrate consumers using /data/env for data_location to another endpoint
  • migrate consumers using /data/env for window_title to use /data/config
  • delete the /data/env endpoint

Secondly, perhaps it makes more sense to just pass path_prefix in the /data/environment in this PR, (app_routing_effects would block on getEnvironment instead of getConfig). That way, the plan would be simpler:

  • migrate consumers using /data/env for data_location to another endpoint

wdyt?

Copy link
Contributor Author

@stephanwlee stephanwlee Dec 5, 2020

Choose a reason for hiding this comment

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

In the broad strokes, we are about aligned.

While /data/environment is not the most meaningful name, it is actually being used for experiment metadata in a worse way (not just data_location).

if experiment_metadata is not None:
environment.update(
{
"experiment_name": experiment_metadata.experiment_name,
"experiment_description": experiment_metadata.experiment_description,
"creation_time": experiment_metadata.creation_time,
}
)

My ideal would be to rename /data/experiment/[:id]/metadata but "environment", in some sense, about the same thing and I would not advocate for the change unless others feel strongly about it.

I would really not want to roll all experiment metadata under experiment-specific endpoint that is meant to serve lists or runs (besides those endpoints don't exist in a meaningful way? /experiments return an empty array all the time)

One follow up work that is required is to remove window_title from /environment and its usage in the page title:

readonly title$ = this.store.pipe(
select(getEnvironment),
map((env) => env.window_title || 'TensorBoard'),
// (it's an empty string when the `--window_title` flag is not set)
distinctUntilChanged()

filter((pathPrefix) => pathPrefix !== null),
take(1),
tap((pathPrefix) => {
this.pathPrefix = pathPrefix;
Copy link
Contributor

Choose a reason for hiding this comment

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

Would things be more maintainable if we didn't store pathPrefix on AppRoutingEffects, but made the Location's getPath, onPopState, replaceState methods aware of it? e.g.

this.location.setPathPrefix(pathPrefix);

I think Location's method surface is much smaller to check than making sure every usage of this.location.getPath() is covered in this file. In case some other file in app_routing needed to inject Location, it wouldn't need to do all this wrapping as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I thought about that but was a bit weary of putting business logic into what seems like a clear API, location!

According to our usages of the state, we need to be able to

  • read synchronously
  • discern was modified

Those one or two are separate APIs that really do not belong in the Location, imo.

@nfelt
Copy link
Contributor

nfelt commented Dec 5, 2020

I'm not crazy about baking the path_prefix string itself into the actual XHR responses of the app. Conceptually the functioning of the app should be independent of the path prefix it's "mounted" under - everything about the path_prefix should be able to be handled by normal WSGI middleware (as we started doing in #2733; this is also a task in #2573).

What William suggests above nicely avoids this dependency on the prefix itself:

Anwyay, what I’ve typically done for this is to include in each page’s
static HTML a relative path reference from that page to the root of the
application

We can use this aspect of the suggestion even if we don't do server-side HTML modification, since we can just change data/environment to return something that indicates the relative path of that particular endpoint to the root (I think it would suffice to either return the literal subpage path like experiment/1234/, an expression representing the path to the root like ../.., or even just an integer for the number of path segments we are below the root).

If we do that, we actually do preserve the property that data/environment is still page-specific, and we avoid introducing an extra XHR in the critical path.

@stephanwlee
Copy link
Contributor Author

stephanwlee commented Dec 5, 2020

If I launch with --path_prefix /foo/bar at this patch and navigate to
/foo/bar/experiment/123/, it redirects me to just /foo/bar/, and
makes initial requests to /foo/bar/experiment/123/data/runs but then
later makes requests to just /foo/bar/data/runs. Is that working as
intended? (Like, it doesn’t matter for the OSS data providers, but it
doesn’t seem right to me.)

No, that is not what actually happens. If you navigate to /foo/bar/experiment/123, it should remain there (no redirection to /foo/bar/if your app is truly aware of /experiment/123/) and run fetch data from/foo/bar/experiment/123/data/*`. Are you referring to an actual behavior? If so, it is not intended and I would appreciate if you can kindly point me to the env that you've used to reproduce this.

Then, when index.js starts, it grabs body.dataset.initialRoot and
records normalize(${pathname}/${initialRoot}/) as the path prefix,
which works independent of the pathname, can be hosted statically, and
doesn’t add another RPC to the critical path.

sounds like what I had recommended in the "dehydrate" section in original comment (#4421 (comment)).
I avoided it for the reason you've mentioned.

What William suggests above nicely avoids this dependency on the prefix itself:

Anwyay, what I’ve typically done for this is to include in each page’s
static HTML a relative path reference from that page to the root of the
application

We can use this aspect of the suggestion even if we don't do server-side HTML modification, since we can just change data/environment to return something that indicates the relative path of that particular endpoint to the root (I think it would suffice to either return the literal subpage path like experiment/1234/, an expression representing the path to the root like ../.., or even just an integer for the number of path segments we are below the root).

Sorry, I don't understand the approach at all. /data/environment returning some information about path_prefix (directly or indirectly) sound about the same and they are on the critical path of application bootstrap no matter what. If you are protesting against the name "path_prefix", I can make some changes but I think it is the best option we've got since (1) we want to surface the app config information anyways and (2) semantically meaningful and straightforward.

Before I address any comments, I want to hear back from passbyers.

@wchargin
Copy link
Contributor

wchargin commented Dec 5, 2020

No, that is not what actually happens. If you navigate to /foo/bar/experiment/123, it should remain there (no redirection to /foo/bar/if your app is truly aware of /experiment/123/) and run fetch data from/foo/bar/experiment/123/data/*`. Are you referring to an actual behavior? If so, it is not intended and I would appreciate if you can kindly point me to the env that you've used to reproduce this.

Yes, this is what I actually observed at d0a9f847c, running:

bazel run //tensorboard -- --logdir ~/tensorboard_data/mnist/ --path_prefix /foo/bar --bind_all

and navigating to /foo/bar/experiment/123/ in my browser. I see these
requests:

Screenshot of network panel showing the requests described in previous comment

(I have tf-nightly-20201204-py3.8, but that shouldn’t matter.)

@wchargin
Copy link
Contributor

wchargin commented Dec 5, 2020

Let me know if you need more info to repro.

@wchargin
Copy link
Contributor

wchargin commented Dec 5, 2020

Sorry, I don't understand the approach at all. /data/environment returning some information about path_prefix (directly or indirectly) sound about the same

Suppose that we’re at /foo/bar/experiment/123 and request the path
prefix info. Say that in case (a), it returns /foo/bar/ and we
interpret that accordingly. In case (b), it returns experiment/123/,
or, equivalently, ../.., and we interpret that accordingly.

@nfelt and I are saying that case (b) is nicer than case (a) because
TensorBoard behaves the same way independent of the path prefix.
All the XHR responses are the same with any --path_prefix value.
For instance, you can run this server and simply scrape all the HTML and
XHRs, then save them to static files and point a static server at them.
In case (b), TensorBoard continues to work if you move that directory
somewhere else relative to the web server. In case (a), it doesn’t.

and they are on the critical path of application bootstrap no matter what.

Agreed, but /data/environment is already on the critical path. If we
add a new /data/config XHR, then that’s an extra XHR on the critical
path, right? This is independent of the (a)/(b) discussion above.

@stephanwlee
Copy link
Contributor Author

stephanwlee commented Dec 5, 2020

@wchargin, Thanks, I can repro. It is very surprising to me that we serve index.html on the /foo/bar/experiment/123/ as opposed to /foo/bar/baz/. I will look into the plugins_listing and subsequent calls being made under /foo/bar/experiment/123/data separately (since it is not related to path_prefix).

@nfelt and I are saying that case (b) is nicer than case (a) because
TensorBoard behaves the same way independent of the path prefix.
All the XHR responses are the same with any --path_prefix value.
For instance, you can run this server and simply scrape all the HTML and
...

That benefit seems very hypothetical and marginal. Response changing given current route (/experiment/123/ vs. /experiment/) and handling such stateful response seems more painful.

Agreed, but /data/environment is already on the critical path. If we
add a new /data/config XHR, then that’s an extra XHR on the critical
path, right? This is independent of the (a)/(b) discussion above.

Is /data/environment really on the critical path? I think /data/plugins_listing and /data/runs are but I am not so sure about the environments. I mean, you still see the spinner while the environment is loading but as soon as plugins listing is loaded, the dashboard view is visible on the screen. Also, in case of multi-experiments world, /data/config is only relevant
on the initial load and it will not matter on the subsequent dashboard navigations.

@wchargin
Copy link
Contributor

wchargin commented Dec 7, 2020

Is /data/environment really on the critical path? I think /data/plugins_listing and /data/runs are but I am not so sure about the environments.

Good point. I stand corrected.

@lukasmasuch
Copy link

lukasmasuch commented Dec 9, 2020

As a tensorboard user, I would very much prefer the solution suggested by @wchargin (case b). Allowing the frontend to work under any relative path independent from the configured path-prefix - e.g. by using window.location.pathname - makes it a lot easier and flexible to integrate into many different setups. For me, it always was a big strength of the previous versions of tensorboard to not have this limitation. Requiring the frontend to use the configured path-prefix of the backend server might have unplanned consequences for many integration scenarios (especially if reverse proxies are involved).

Other complex tools, such as code-server, are also able to have their frontend run under a relative path. As far as I know, they are also using the window.location.pathname to construct the base URL (see here).

To add an example which, as far as I understood, would not work with the implemented solution:

  • Service 1: Starts tensorboard with prefix /tool/tensorboard
  • Service 2: Reverse proxy that redirects all requests coming in on /experiments to service 1 to the path /tool/tensorboard
  • User accesses tensorboard in the browser on path /experiments

Copy link
Contributor

@psybuzz psybuzz left a comment

Choose a reason for hiding this comment

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

Marking as "request changes", to await feedback on a new approach using server side provided path.

@stephanwlee stephanwlee force-pushed the pp branch 3 times, most recently from 9b600a5 to ccf0685 Compare December 11, 2020 02:49
Copy link
Contributor Author

@stephanwlee stephanwlee left a comment

Choose a reason for hiding this comment

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

@nfelt PTAL and see if this aligns with what you were thinking of.

@psybuzz do note that after index.html, our header appeared a bit different :\ I could not discern exactly what has caused the regression so I had to apply flex.

image

@stephanwlee stephanwlee force-pushed the pp branch 2 times, most recently from 685e807 to 43341bc Compare December 11, 2020 05:20
Copy link
Contributor

@nfelt nfelt left a comment

Choose a reason for hiding this comment

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

Yeah this looks good, thanks. (I think we'll eventually want to avoid CorePlugin directly using path_prefix but I'm happy to clean that up later.)

@stephanwlee stephanwlee requested review from nfelt and psybuzz December 14, 2020 20:14
Copy link
Contributor

@psybuzz psybuzz left a comment

Choose a reason for hiding this comment

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

Side note: I think we may need router_link_directive_container.ts to become aware of AppRoots as well. Currently, when an <a> has [routerLink]=['foo'], we guarantee that the "href" attribute is a full, correct URL so things like ctrl+click properly open "http://tb/foo" in a new tab.
With path_prefix, I would imagine those hrefs now point to incorrect appRoot-less URLs.

Copy link
Contributor

@nfelt nfelt left a comment

Choose a reason for hiding this comment

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

Python changes and tests LGTM, thanks. Will defer to Erik's remaining comments before approving though.

Comment on lines 141 to 149
# Technically, it is possible to flush parts using yields but http_utils
# try to measure gzip and measure content length if a truthy value is
# passed.
res = http_util.Respond(request, None, "text/html")
res.stream.write(
'<meta name="tb-relative-root" content="%s/" />' % (relpath)
)
res.stream.write(index_asset_bytes)
return res
Copy link
Contributor

@wchargin wchargin Dec 17, 2020

Choose a reason for hiding this comment

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

I don’t quite understand why this change implies the accompanying change
to http_util.Respond. The alternative that comes to mind is:

meta_header = '<meta name="tb-relative-root" content="%s/" />' % (relpath)
content = meta_header.encode("utf-8") + index_asset_bytes
return http_util.Respond(request, content, "text/html")

Judging from the comment, it sounds like you want to avoid that because
it automatically gzips, since the content type is text/html. That
makes sense. But then can’t we just pass content_encoding="identity"
rather than adding new mechanisms? I think that that already does the
right thing:

>>> from tensorboard.backend import http_util
>>> from werkzeug import test as wtest
>>> from werkzeug import wrappers
>>> def req():
...     headers = [("Accept-Encoding", "gzip")]
...     environ = wtest.EnvironBuilder(headers=headers).get_environ()
...     return wrappers.Request(environ)
... 
>>> res = http_util.Respond(req(), b"<html>\n", "text/html")
>>> res.headers.get("Content-Encoding"), res.get_data()
('gzip', b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xb3\xc9(\xc9\xcd\xb1\xe3\x02\x00\x994\xcb3\x07\x00\x00\x00')
>>> res = http_util.Respond(req(), b"<html>\n", "text/html", content_encoding="identity")
>>> res.headers.get("Content-Encoding"), res.get_data()
('identity', b'<html>\n')

Failing that, I might prefer to add a new no_gzip flag or something
rather than introducing (a) the edge case in http_util and (b) the
stream-writing code in this method.

@stephanwlee
Copy link
Contributor Author

Tested the sync (cl/347955446) and tested against the internal version of TB.

@stephanwlee stephanwlee requested a review from psybuzz December 17, 2020 17:29
Copy link
Contributor

@psybuzz psybuzz left a comment

Choose a reason for hiding this comment

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

The frontend code lgtm, thanks for checking internally.

I also did a quick test sync and saw 1 projector test fail, but I'm not 100% sure its related: cl/348085083

Approving % wchargin@'s approval on the content_encoding=identity topic.

Copy link
Contributor

@wchargin wchargin left a comment

Choose a reason for hiding this comment

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

Python revisions look good to me. Ideally I would like html.escape
around the HTML interpolation; even though there’s probably an argument
that it’s safe in this case due to provenance of relpath, it’s nice
for it to be “clearly correct” rather than “merely correct”.

Copy link
Contributor

@nfelt nfelt left a comment

Choose a reason for hiding this comment

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

Took a quick look and the updates and generally LGTM. +1 to William's point that it would be a bit better to escape the inserted relative path, just in case.

pathname: this.pathname,
});
return (
this.appRoot.slice(0, -1) +
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it maybe make sense to expose getAbsPathnameWithAppRoot() somewhere to kind of share this logic? As it is, it's not terribly obvious here why we slice the last char off appRoot unless you can see the bigger picture. Maybe at least a comment would help.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. Moved it to AppRootProvider instead.

Problem:
TensorBoard has a flag, path_prefix, which serves frontend and the data
endpoints at your specified, if the flag is passed, pathname. While
exact use case slips my mind, it is certainly used by dependents. Now,
the problem is that new Angular based TensorBoard has a custom router
for in-app navigation (we may add new routes for experiment list and
others) it is unaware of the path prefix.

Router works by matching set of predefined routes to current pathname
and finds a match. However, the configuration is unaware (rightfully) of
the path prefix and it results in zero match, which in turn cause
navigation to the default path, "/". This totally breaks the assumption
of the path prefix as all the request from the frontend will be sent to
"/data/..." as opposed to "[path_prefix]/data/..."

Remediation:
Previously, there was no reason to propagate the prefix information to
the frontend. Application only did hash changes and never required a
navigation. With this change, we now:

- server side render meta tag that depicts the relative path to the root of the
  application
- bootstrap Angular app from the relative root path and uses that information
  for matching routes and navigation

Tests:
- manually tested with/without path_prefix
- tested dynamic plugin with/without path_prefix (projector)
@stephanwlee
Copy link
Contributor Author

@psybuzz made significant changes to the FE code to adopt the feedback. Please TAL one last time.

@@ -54,6 +56,10 @@ function isNavigation(

@Injectable()
export class Location implements LocationInterface {
Copy link
Contributor

Choose a reason for hiding this comment

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

One suggestion: perhaps we should add a comment to appRootProvider.getFullPathFromRouteOrNav(), noting that this.appRootProvider.getAbsPathnameWithAppRoot() is required to get the "full full" path.

@stephanwlee stephanwlee merged commit 0fe35b5 into tensorflow:master Jan 13, 2021
@stephanwlee stephanwlee deleted the pp branch January 13, 2021 22:55
@wchargin wchargin mentioned this pull request Jan 13, 2021
2 tasks
wchargin pushed a commit that referenced this pull request Jan 14, 2021
Problem:
TensorBoard has a flag, path_prefix, which serves frontend and the data
endpoints at your specified, if the flag is passed, pathname. While
exact use case slips my mind, it is certainly used by dependents. Now,
the problem is that new Angular based TensorBoard has a custom router
for in-app navigation (we may add new routes for experiment list and
others) it is unaware of the path prefix.

Router works by matching set of predefined routes to current pathname
and finds a match. However, the configuration is unaware (rightfully) of
the path prefix and it results in zero match, which in turn cause
navigation to the default path, "/". This totally breaks the assumption
of the path prefix as all the request from the frontend will be sent to
"/data/..." as opposed to "[path_prefix]/data/..."

Remediation:
Previously, there was no reason to propagate the prefix information to
the frontend. Application only did hash changes and never required a
navigation. With this change, we now:

- server side render `<meta>` tag that depicts the relative path to the root of
  the application.
- bootstrap Angular app from the relative root path and uses that information
  for matching routes and navigation
wchargin added a commit that referenced this pull request Jan 14, 2021
Backport of #4423 to 2.4. Cf. #4547.

---

Problem:
TensorBoard has a flag, path_prefix, which serves frontend and the data
endpoints at your specified, if the flag is passed, pathname. While
exact use case slips my mind, it is certainly used by dependents. Now,
the problem is that new Angular based TensorBoard has a custom router
for in-app navigation (we may add new routes for experiment list and
others) it is unaware of the path prefix.

Router works by matching set of predefined routes to current pathname
and finds a match. However, the configuration is unaware (rightfully) of
the path prefix and it results in zero match, which in turn cause
navigation to the default path, "/". This totally breaks the assumption
of the path prefix as all the request from the frontend will be sent to
"/data/..." as opposed to "[path_prefix]/data/..."

Remediation:
Previously, there was no reason to propagate the prefix information to
the frontend. Application only did hash changes and never required a
navigation. With this change, we now:

- server side render meta tag that depicts the relative path to the root of the
  application
- bootstrap Angular app from the relative root path and uses that information
  for matching routes and navigation

Tests:
- manually tested with/without path_prefix
- tested dynamic plugin with/without path_prefix (projector)

Co-authored-by: Stephan Lee <[email protected]>
@wchargin wchargin mentioned this pull request Jan 14, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants