Conversation
rmars
left a comment
There was a problem hiding this comment.
nice! great start. if you merge master into this branch, the props logic in ResourceDetail might help with some of the problems you're seeing here.
left a couple comments below.
web/app/js/components/Sidebar.jsx
Outdated
| { | ||
| _.map(_.sortBy(sidebarComponents[resourceName], r => r.resource.name), ro => { | ||
| return ( | ||
| <Menu.Item key={"/namespaces/" + ro.resource.namespace + "/" + ro.resource.type + "s/" + ro.resource.name}> |
There was a problem hiding this comment.
we can increase readability by refactoring the "/namespaces/" + ro.resource.namespace + "/" + ro.resource.type + "s/" + ro.resource.name so we don't repeat this particular string so many times here
web/app/js/components/Sidebar.jsx
Outdated
| return {nsValue: ns, nsName: ns}; | ||
| })); | ||
| let sidebarComponents = this.filterResourcesByNamespace(this.state.resourceGroupings, this.state.namespaceFilter); | ||
| const SubMenu = withRouter(Menu.SubMenu); |
There was a problem hiding this comment.
this is causing a console error that is hiding the real problems :)
There was a problem hiding this comment.
🤦♂️ Making things worse for myself. Thanks for catching that.
| </PrefixedLink> | ||
| </Menu.Item> | ||
|
|
||
| <Menu.Item className="sidebar-menu-item" key="/namespaces"> |
There was a problem hiding this comment.
we still want to access the list pages (/namespaces, /deployments, /pods) etc. from somewhere so let's make sure these don't get lost
There was a problem hiding this comment.
Good point. I wasn't sure whether those pages would be needed since the sidebar partially "becomes" the namespace page. Although, I do agree that we still want to be able to view stats about requests between namespaces since we lose visibility of these stats if we rely solely on the sidebar.
| : null | ||
| } | ||
|
|
||
| <Menu.SubMenu |
There was a problem hiding this comment.
we should include some way to access these list pages in the nav
web/app/js/components/Sidebar.jsx
Outdated
| let namespaces = [{nsValue: "all", nsName: "All Namespaces"}].concat(_.map(this.state.namespaces, ns => { | ||
| return {nsValue: ns, nsName: ns}; | ||
| })); | ||
| let sidebarComponents = this.filterResourcesByNamespace(this.state.resourceGroupings, this.state.namespaceFilter); |
There was a problem hiding this comment.
authorities are kind of different from the other resources and it makes less sense to have this type of dashboard for them; also they're not linked in the web server routes to going to http://localhost:8084/namespaces/linkerd/authoritys/10.1.1.97:9995 won't go anywhere. We should filter out the resource types that we don't have resource detail pages for (or add routing for them in web/srv/server.go, currently there's only three resource types there)
web/app/js/components/Sidebar.jsx
Outdated
| <span>{ns} {this.state.collapsed ? "namespace" : ""}</span> | ||
| </PrefixedLink> | ||
| </Menu.Item> | ||
| <SubMenu |
There was a problem hiding this comment.
if there are no sub items in the menu, we should either omit or disable linking for the high level component (e.g. there aren't any deployments when you select namespace kube-system and so the nave is misleading; it takes a lot of time to click on each resource type and figure out that there aren't resources in it)
There was a problem hiding this comment.
Good point. I will get that set up on my next commit.
8ba3360 to
e65881b
Compare
|
There are a few things that have been mentioned in #1449 that I think should be in a separate PR. I am thinking this PR is a good starting point for the sidebar. I will create issues for the additional work needed to flesh the sidebar out. |
web/app/test/SidebarTest.jsx
Outdated
| import sinonStubPromise from "sinon-stub-promise"; | ||
| import Enzyme, { mount } from "enzyme"; | ||
|
|
||
|
|
web/app/js/components/Sidebar.jsx
Outdated
| .then(([versionRsp, allRsp, nsRsp]) => { | ||
| let statTables = _.get(allRsp, ["ok", "statTables"], []); | ||
| let rows = _.flatMap(statTables, tb => tb.podGroup.rows); | ||
| let resourceGroupings = _.groupBy(rows, r => r.resource.type); |
There was a problem hiding this comment.
you can get this by using processMultiResourceRollup:
import { processMultiResourceRollup } from './util/MetricUtils.js';
...
let statsByResource = processMultiResourceRollup(allRsp)
The object format is a little different, so you'd be needing to do r.name instead of r.resource.name in all the places you do r.resource, for example.
There was a problem hiding this comment.
Oh nice! this is great. Thanks for pointing that out.
web/app/js/components/Sidebar.jsx
Outdated
| let rows = _.flatMap(statTables, tb => tb.podGroup.rows); | ||
| let resourceGroupings = _.groupBy(rows, r => r.resource.type); | ||
| let nsStats = _.get(nsRsp, ["ok", "statTables", 0, "podGroup", "rows"], []); | ||
| let namespaces = _(nsStats).map(r => r.resource.name).sortBy().value(); |
There was a problem hiding this comment.
I realize this is existing code, but we can also get this by using processSingleResourceRollup:
let nsStats = processSingleResourceRollup(nsRsp);
let namespaces = _(nsStats).map('name').sortBy().value();
web/app/js/components/Sidebar.jsx
Outdated
| { | ||
| _.map(namespaces, ns => { | ||
| return ( | ||
| <Select.Option key={ns.nsValue} value={ns.nsValue}>{ns.nsName}</Select.Option> |
There was a problem hiding this comment.
ns.nsValue is a little repetitive, maybe ns.labelValue and ns.labelName? or simply ns.name and ns.value?
_.map(namespaces, label => ... label.name .... label.value might also be a clearer option
| } | ||
|
|
||
| // Filters resources retrieved from a stat query by namespaces. Does not include resources of type "authority" | ||
| filterResourcesByNamespace(resources, namespace) { |
There was a problem hiding this comment.
We could get rid of the resources we don't want to show when we process the metrics in loadFromServer. This could just be delete resourceGroupings["authority"] there, for example. This removes the need for the deleting of it in this method.
I'll also note that services aren't wired in, so you might want to handle that too.
| <Layout.Sider | ||
| width="260px" | ||
| breakpoint="lg" | ||
| collapsed={this.state.collapsed} |
There was a problem hiding this comment.
Per discussion on the ticket, I think we'll want to retain the ability to collapse (or hide) the sidebar.
|
yea, good point. Do you think its still ok to have the prefix when we are viewing resources from a single namespace? |
|
I actually don't mind the name duplication for all namespaces. I'd rather have the view identical between the filter selections than prefix only in this instance. It feels like a problem we're just creating for ourselves right now. That said, if @rmars feels particularly passionately about it, I don't see why we can't do it. |
|
I really prefer the namespace prefix. If we don't want discrepancies between the views, I'd rather have it prefixed in all cases :) |
60a4c2c to
30f9090
Compare
rmars
left a comment
There was a problem hiding this comment.
sweeeet. the success rate dots look pretty cool!
web/app/js/components/Sidebar.jsx
Outdated
| import { linkerdLogoOnly, linkerdWordLogo } from './util/SvgWrappers.jsx'; | ||
| import './../../css/sidebar.css'; | ||
|
|
||
| const getSrClassification = sr => { |
There was a problem hiding this comment.
we have a similar method to this in like three places in the codebase now. I think we should try to consolidate this in a helper function in MetricUtils.js
There was a problem hiding this comment.
Yea good call. We probably should make the method accept a map that are associated status labels to good, ok and bad so we can use arbitrary css classNames.
| }; | ||
|
|
||
| export const excludeResourcesFromRollup = (rollupMetrics, resourceTypeList) => { | ||
| for (let i = 0; i < resourceTypeList.length; i++) { |
There was a problem hiding this comment.
I think this would be more readable as
export const excludeResourcesFromRollup = (rollupMetrics, resourcesToExclude) => {
_.each(resourcesToExclude, resource => {
delete rollupMetrics[resource];
})
| @@ -32,6 +32,10 @@ ul.ant-menu.ant-menu-sub { | |||
| & .update { | |||
web/app/js/components/Sidebar.jsx
Outdated
| <Menu.SubMenu | ||
| className="sidebar-menu-item" | ||
| key={resourceName} | ||
| disabled={sidebarComponents[resourceName].length === 0} |
There was a problem hiding this comment.
nit: _.isEmpty(sidebarComponents[resourceName])
web/app/js/components/Sidebar.jsx
Outdated
| let PrefixedLink = this.api.PrefixedLink; | ||
| let numHiddenNamespaces = _.size(this.state.namespaces) - this.state.maxNsItemsToShow; | ||
|
|
||
| let namespaces = [{value: "all", name: "All Namespaces"}].concat(_.map(this.state.namespaces, ns => { |
There was a problem hiding this comment.
readability nit:
let namespaces = [
{value: "all", name: "All Namespaces"}
].concat(_.map(this.state.namespaces, ns => {
return { value: ns, name: ns };
}));
web/app/js/components/Sidebar.jsx
Outdated
| return _.mapValues(resources, o => { | ||
| return _(o) | ||
| .filter(r => r.namespace === namespace && r.added) | ||
| .value(); |
There was a problem hiding this comment.
if we're only using one _ function here, there's no need to wrap o and then unwrap it with value... so this could be written as return _.filter(o, r => r.namespace === namespace && r.added)
web/app/js/components/Sidebar.jsx
Outdated
| this.setState({namespaceFilter: value}); | ||
| } | ||
|
|
||
| // Filters resources retrieved from a stat query by namespaces. Does not include resources of type "authority" |
web/app/js/components/Sidebar.jsx
Outdated
| if (namespace === "all") { | ||
| return _.mapValues(resources, o => { | ||
| return _(o) | ||
| .filter(r => r.added) |
There was a problem hiding this comment.
the only difference between these is this line... what if we refactored the repeated code by putting the filter into a named variable?
filterResourcesByNamespace(resources, namespace) {
let filter = namespace === "all" ? r => r.added :
r => r.added && r.namespace === namespace;
return _.mapValues(resources, res => _.filter(res, filter));
}
There was a problem hiding this comment.
Slick! I like it!
web/app/js/components/Sidebar.jsx
Outdated
| { | ||
| _.map(_.sortBy(sidebarComponents[resourceName], r => `${r.namespace}/${r.name}`), r => { | ||
| // only display resources that have been meshed | ||
| if (r.added) { |
There was a problem hiding this comment.
do we need this if here if we've filtered out the unadded components in filterResourcesByNamespace?
web/app/js/components/Sidebar.jsx
Outdated
| to={this.generateResourceURL(r)}> | ||
| {`${r.namespace}/${r.name}`} | ||
| </PrefixedLink> | ||
| <Badge status={getSrClassification(r.successRate)} /> |
Signed-off-by: Dennis Adjei-Baah <[email protected]>
Signed-off-by: Dennis Adjei-Baah <[email protected]>
Signed-off-by: Dennis Adjei-Baah <[email protected]>
Signed-off-by: Dennis Adjei-Baah <[email protected]>
Signed-off-by: Dennis Adjei-Baah <[email protected]>
Signed-off-by: Dennis Adjei-Baah <[email protected]>
Signed-off-by: Dennis Adjei-Baah <[email protected]>
Signed-off-by: Dennis Adjei-Baah <[email protected]>
Signed-off-by: Dennis Adjei-Baah <[email protected]>
Signed-off-by: Dennis Adjei-Baah <[email protected]>
30f9090 to
116b14d
Compare
web/app/js/components/Sidebar.jsx
Outdated
| title={<span>{friendlyTitle(resourceName).plural}</span>}> | ||
| { | ||
| _.map(_.sortBy(sidebarComponents[resourceName], r => `${r.namespace}/${r.name}`), r => { | ||
| // only display resources that have been meshed` |
| }); | ||
| }; | ||
|
|
||
| const generateResourceURL = r => { |
| return successRateLabels.bad; | ||
| } else if (rate < 0.95) { | ||
| return successRateLabels.neutral; | ||
| } else {return successRateLabels.good;} |
There was a problem hiding this comment.
spacing nit:
else {
return successRateLabels.good;
}
| _.map(_.keys(sidebarComponents).sort(), resourceName => { | ||
| return ( | ||
| <Menu.SubMenu | ||
| className="sidebar-menu-item" |
There was a problem hiding this comment.
TIOLI: I think it would help usability to add a title={`${r.namespace}/${r.name}`} here so that the user can hover over and see the full name in case the name is truncated (probably not super important, but could be nice to have).
There was a problem hiding this comment.
Makes total sense and I feel it is necessary. Thanks for pointing that out! 🙏
Signed-off-by: Dennis Adjei-Baah <[email protected]>
| <Menu.SubMenu | ||
| className="sidebar-menu-item" | ||
| key={resourceName} | ||
| title={<span>{friendlyTitle(resourceName).plural}</span>}> |
There was a problem hiding this comment.
sorry I didn't catch this before, but title tags contain text only and ignore tags within the element.
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/title
But also, unlike the pod names, I don't think there's any case where the full Resource Title will be cut off in the sidebar as is. So I think we should remove this title= entirely.
I can do that in a small branch of misc fixes I'm working on.
There was a problem hiding this comment.
Ah! thanks for catching that. I wasn't aware that the title prop was going to be the page title. I assumed it was something that antd used for displaying the Menu.SubMenu. Thanks for taking care of this!
There was a problem hiding this comment.
oh, blah, wrong link, I meant the attribute... buries face
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/title




Linkerd CLI's "look and feel" is similar to Kubernetes
kubectlCLI. Linkerd's dashboard can be extended to match Kubernetes dashboard UI.This PR serves as a starting point for this work. The new sidebar shows all resources from all namespaces on initial page load. Resources can be filtered to show only items in a given namespace. The sidebar displays authority, deployment, service and, pod resources. We may need to think about whether it is necessary to show all resources types. Some resources, i.e. authorities, contain a large cardinality of resource details and may not be very useful to a user.
fixes #1449