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

Redesigned Overview #1335

Merged
merged 1 commit into from
Mar 16, 2017
Merged

Redesigned Overview #1335

merged 1 commit into from
Mar 16, 2017

Conversation

spadgett
Copy link
Member

@spadgett spadgett commented Mar 9, 2017

https://trello.com/c/QxbqJsoK

Updated overview using the Patternfly list view with rows that expand to reveal more detail.

screen shot 2017-03-09 at 5 02 30 pm

screen shot 2017-03-09 at 6 52 11 pm

Contains code from @rhamilto. Based on designs from @ajacobs21e @lizsurette and @ncameronbritt

Fixes #236
Fixes #619
Fixes #658
Fixes #729

Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1389458
Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1401134

TODO

  • Generalize some directives so they're not specific to the overview.
  • Add in-view logic back to overview metrics
  • Move getBreakpoint to a service
  • Use namespaced resize event listener
  • Make view-by local storage key specific to overview
  • Don't show scaled down replication controllers from a deployment config that was deleted
  • Add some missing canI checks for kebab actions
  • Hide kebab menu when none of the actions are visible
  • Fix bug where filtering doesn't trigger in-view updates for metrics
  • Remove renderOptions
  • Colorize and linkify mini log
  • Use row.state.breakpoint rather than hidden-* classes to show and hide the metrics summary
  • Reorder DataService calls
  • Order RCs by deployment number instead of date
  • Test failed rollouts of replica sets
  • Fix pipeline sorting
  • Consistently use DEFAULT_POLL_INTERVAL
  • Fix visibleReplicaSets typo
  • get/setBuildConfigsForDeploymentConfig -> get/setBuildConfigsForObject
  • Don't pass dummy pod template to LabelSelector.matches
  • Don't sort recentPipelinesByBuildConfig in the view
  • Use AlertMessageService.permanentlyHideAlert in ResourceAlertsService
  • Fall back to comparing names if service weights are equal when sorting
  • Fix initial tab selection for mobile when there are no services
  • Improve "deployment no longer latest" error message
  • Handle NaN deployment versions when sorting
  • Show < 0.01 cores for very small CPU usage

Follow-on Work

  • Support types other than deployment configs for pipeline annotation pipeline.alpha.openshift.io/uses
  • Show headless services
  • Open issue for removing previous overview code -- Tracked by Remove previous overview code #220
  • Show basic route info in collapsed state
  • Make cancel deployment dialog generic and reuse it on the deployment config page

@spadgett spadgett requested a review from jwforres March 9, 2017 22:04
@spadgett
Copy link
Member Author

spadgett commented Mar 9, 2017

cc @sspeiche @jorgemoralespou

@spadgett spadgett force-pushed the overview branch 3 times, most recently from 8fe857b to d3bf28b Compare March 10, 2017 00:08
Copy link
Member

@jwforres jwforres left a comment

Choose a reason for hiding this comment

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

Not done with newOverview.js yet, will continue later. This is what I've got for now.

});
AlertMessageService.clearAlerts();

var getBreakpoint = function() {
Copy link
Member

Choose a reason for hiding this comment

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

Feels like this function could move into a filter or service for us to use elsewhere, guessing this won't be the last time we need to do it

}
}, 50);

$(window).on('resize', onResize);
Copy link
Member

Choose a reason for hiding this comment

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

use a scoped event, 'overview.resize'

}];
overview.filterBy = 'name';

overview.viewByOptions = [{
Copy link
Member

Choose a reason for hiding this comment

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

i'm debating whether these should be under renderOptions

overview.renderOptions = {
  ...
  filterBy
  filterByOptions
  viewByOptions
  ...
}

I know historically renderOptions were only things we names consistently across pages. But these seem like things that probably should be named consistently across pages :)

Copy link
Member Author

Choose a reason for hiding this comment

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

It's funny, I was debating removing renderOptions since some directives that don't have isolated scope look for it. For instance,

https://github.com/openshift/origin-web-console/blob/master/app/scripts/directives/nav.js#L150-L154

The settings here apply only to the overview, but renderOptions implies that they might be used outside of this controller. We have some copied code in other controllers as well that sets renderOptions when not needed.

As an aside, we might remove that check from project-filter. I don't think it's needed anymore since it was removed from the nav a while back?

Copy link
Member

Choose a reason for hiding this comment

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

Good point, we may not even need renderOptions any more

};

// Group a collection of resources by app label. Returns a map where the key
// is the app label value and the value is an array of object, sorted by
Copy link
Member

Choose a reason for hiding this comment

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

typo, objects

// is the app label value and the value is an array of object, sorted by
// `metadata.name`.
var groupByApp = function(collection) {
return AppsService.groupByApp(collection, 'metadata.name');
Copy link
Member

Choose a reason for hiding this comment

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

Can this be more generic and put it in a LabelsService instead? then you pass it which label key you want to group by. Or was the thinking that if we kept it as group by app but changed how the grouping worked that it would just be under this same service.

Copy link
Member Author

Choose a reason for hiding this comment

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

Or was the thinking that if we kept it as group by app but changed how the grouping worked that it would just be under this same service.

Yeah, that was the thought, although I'm OK moving it to LabelsService as well.

Copy link
Member

Choose a reason for hiding this comment

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

Ok thats fine, I think I'm ok with it either way.

};

// Track view-by state in localStorage.
var viewByKey = $routeParams.project + '/view-by';
Copy link
Member

Choose a reason for hiding this comment

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

make this key specific to overview in case we add a view-by somewhere else later

}

var isPod = function(apiObject) {
return apiObject && apiObject.kind === 'Pod';
Copy link
Member

Choose a reason for hiding this comment

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

not necessarily better but could also do

_.isMatch(apiObject, { kind: 'Pod' });

var visibleReplicationControllers = getVisibleReplicationControllers(deploymentConfig);
_.each(visibleReplicationControllers, function(replicationController) {
var rcNotifications = getNotifications(replicationController);
_.assign(notifications, rcNotifications);
Copy link
Member

Choose a reason for hiding this comment

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

if multiple visible RCs have the same notifications cant these clash? do the keys for the RC notifications that come back have unique keys for the object?

Copy link
Member Author

Choose a reason for hiding this comment

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

The alert ID for RC notifications contains the object UID. (And for pods, we have the grouping logic.)

var rcByDC = {};
_.each(overview.replicationControllers, function(replicationController) {
var dcName = getDeploymentConfigName(replicationController) || '';
if (!dcName || !overview.deploymentConfigs[dcName]) {
Copy link
Member

Choose a reason for hiding this comment

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

does this mean if i delete a DC and it doesn't cascade to delete the RCs that I'm going to see a bunch of old RCs showing up that aren't scaled up?

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks, fixed.

@spadgett
Copy link
Member Author

Not done with newOverview.js yet, will continue later. This is what I've got for now.

Thanks @jwforres. I added a checklist for feedback and follow-on work to the description, and I have local changes for most of your comments to this point. Let me know if you'd like me to push.

@spadgett
Copy link
Member Author

@jwforres Noticed I'm missing some canI checks for the actions kebab. Working on adding these.

@spadgett
Copy link
Member Author

@jwforres Should we reorder the builds and pipelines menu items in the nav to match the order in the overview?

screen_shot_2017-03-10_at_11_54_18_am

@jwforres
Copy link
Member

I would hesitate to switch nav order on people if we can avoid it. Muscle memory is a thing :)

Copy link
Member

@jwforres jwforres left a comment

Choose a reason for hiding this comment

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

finished the first pass of newOverview.js


// Sort the visible replication controllers.
_.each(rcByDC, function(replicationControllers, dcName) {
var ordered = orderObjectsByDate(replicationControllers, true);
Copy link
Member

Choose a reason for hiding this comment

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

I remember on builds we hit an issue when we got back two builds with the same timestamp, same in theory is possible on triggered RCs. Should we have a secondary sort that we can pass to orderObjectsByDate, in this case secondary sort would be the deployment number descending. I'd also be ok opening a follow up issue for this one since I think its a general problem.

}

var deployment = overview.deployments[deploymentName];
var visibleRelicaSets = _.filter(replicaSets, function(replicaSet) {
Copy link
Member

Choose a reason for hiding this comment

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

typo visibleReplicaSets, here and below

});
var ordered = DeploymentsService.sortByRevision(visibleRelicaSets);
overview.replicaSetsByDeployment[deploymentName] = ordered;
// TODO: Need to check if this really works for failed / canceled rollouts.
Copy link
Member

Choose a reason for hiding this comment

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

was this an existing TODO or do you still need to resolve this?

var visibleRelicaSets = _.filter(replicaSets, function(replicaSet) {
return isReplicaSetVisible(replicaSet, deployment);
});
var ordered = DeploymentsService.sortByRevision(visibleRelicaSets);
Copy link
Member

Choose a reason for hiding this comment

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

so you are sorting by revision here but ordering by date on RCs for a DC, can't we just do this same thing for DCs then to fix the potential issue with colliding timestamps

// TODO: Need to check if this really works for failed / canceled rollouts.
// It might need to be reworked.
overview.currentByDeployment[deploymentName] = _.head(ordered);
// var deploymentRevision = DeploymentsService.getRevision(deployment);
Copy link
Member

Choose a reason for hiding this comment

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

is this commented out code part of checking the TODO above?

_.set(overview, ['deploymentConfigsByPipeline', bcName], dcNames);
_.each(dcNames, function(dcName) {
state.pipelinesForDeploymentConfig[dcName] = state.pipelinesForDeploymentConfig[dcName] || [];
state.pipelinesForDeploymentConfig[dcName].push(buildConfig);
Copy link
Member

Choose a reason for hiding this comment

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

should this be sorted

Copy link
Member Author

Choose a reason for hiding this comment

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

should this be sorted

It's not displayed directly, although recentPipelinesByDeploymentConfig is.

}

// Reset these maps.
overview.recentPipelinesByBuildConfig = {};
Copy link
Member

Choose a reason for hiding this comment

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

its a little confusing from a maintenance perspective that some of these maps are on "state" and some of them are directly on overview. I get that state only needs to have the things that have to be passed down into the list row, just seems easy to typo down the road when making changes.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm hesitant to put things in state that aren't needed by list row. Then it's not obvious when you change something here if it impacts the list row component.

Copy link
Member

Choose a reason for hiding this comment

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

Ok thats fine

$scope.project = project;
state.context = context;

var updateReferencedImageStreams = function() {
Copy link
Member

Choose a reason for hiding this comment

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

should we pull this up out of the watches since all the other functions are defined above?

Copy link
Member Author

Choose a reason for hiding this comment

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

It needs access to context returned from ProjectsService.get :/

Copy link
Member

Choose a reason for hiding this comment

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

Ok probably something we should refactor later then, it doesn't really need the context, i think it really just needs to know the project

overview.horizontalPodAutoscalers = hpaData.by("metadata.name");
groupHPAs();
Logger.log("autoscalers (subscribe)", overview.horizontalPodAutoscalers);
}, {poll: limitWatches, pollInterval: 60 * 1000}));
Copy link
Member

Choose a reason for hiding this comment

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

any reason you didn't switch these next few to use DEFAULT_POLL_INTERVAL

Copy link
Member Author

Choose a reason for hiding this comment

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

No, that was an oversight.

state.limitRanges = response.by("metadata.name");
});

watches.push(DataService.watch("imagestreams", context, function(imageStreamData) {
Copy link
Member

Choose a reason for hiding this comment

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

thinking we should have all the watches first, then the limited watches, then the always polled, then the lists, and then the gets. I think most of them were in that order besides this one.

@spadgett
Copy link
Member Author

@jwforres I've updated the build sorting with a BuildsService.sortBuilds method we can reuse on the build config page. Right now it has the same problem where the builds are in the wrong order if creationTimestamp is the same for both.

@spadgett
Copy link
Member Author

@jwforres I believe I've addressed all comments to this point

Copy link
Member

@jwforres jwforres left a comment

Choose a reason for hiding this comment

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

made it through the JS

});

if (buffer.length > numLines) {
buffer = _.takeRight(buffer, numLines);
Copy link
Member

Choose a reason for hiding this comment

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

if we get a flood of messages this could generate a lot of unnecessary array operations, when we only care about the last chunk. Although if we skip over showing anything then you won't get the scroll through effect of seeing it logged out and disappearing, so i'm not sure there is a better alternative.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, not sure what's best. At least in this case, the buffer is max 8 lines, so we aren't ever keeping too much data in memory. And we're only calling _.takeRight on a small array.

notification.byType = _.mapValues(byType, function(alerts) {
return _.map(alerts, function(alert) {
return _.escape(alert.message);
}).join('<br>');
Copy link
Member

Choose a reason for hiding this comment

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

does it need a double <br> to get whitespace between the messages? might make the popup too big in some cases i guess

if (!_.isEmpty(row.services)) {
_.set(row, 'selectedTab.networking', true);
} else {
_.set(row, 'selectedTab.networking', true);
Copy link
Member

Choose a reason for hiding this comment

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

this if and else are resulting in the same thing, looks like the first one is supposed to be different

}, { alerts: row.state.alerts });
};

// TODO: Pulled from dc.js, but we should probably make the dialog generic and reuse for the deployment config page.
Copy link
Member

Choose a reason for hiding this comment

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

agreed

Copy link
Member Author

Choose a reason for hiding this comment

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

agreed

Added a follow on item to use the same dialog for the deployment config page.

if (replicationController.metadata.uid !== row.current.metadata.uid) {
row.state.alerts["cancel-deployment"] = {
type: "error",
message: "Deployment " + rcName + " no longer latest."
Copy link
Member

Choose a reason for hiding this comment

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

do you want rcName here or the version number? also this isnt a complete sentence, "is no longer latest" would be better, maybe "is no longer the latest"

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

screen shot 2017-03-14 at 7 32 57 pm

return 1;
}

return right.weight - left.weight;
Copy link
Member

Choose a reason for hiding this comment

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

if weights are equal fallback to name

}

leftVersion = parseInt(leftVersion, 10);
rightVersion = parseInt(rightVersion, 10);
Copy link
Member

Choose a reason for hiding this comment

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

shouldn't we handle bad version annotations that parse as NaN? would expect these to be treated like non-existing versions

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, they should parse as NaN. The second parameter is radix.

Copy link
Member

Choose a reason for hiding this comment

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

Right but if they parse as NaN don't we want to fall back to sorting by name?

Copy link
Member Author

Choose a reason for hiding this comment

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

Right but if they parse as NaN don't we want to fall back to sorting by name?

Fixed

var hideAlert = function(alertID, namespace) {
var key = alertHiddenKey(alertID, namespace);
localStorage.setItem(key, 'true');
};
Copy link
Member

Choose a reason for hiding this comment

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

isnt this duplicating these that we already had put into AlertMessageService, which is actually referenced in the quota warnings below.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, this code was moved from serviceGroupNotifications, which probably was written before the helpers were added to AlertMessageService. I'll update it to use the AlertMessageService methods.

@spadgett
Copy link
Member Author

Addressed latest comments.

.text-muted();
text-transform: uppercase;
.sublabel {
font-size: (@component-label - 1);
Copy link
Member

Choose a reason for hiding this comment

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

not seeing where @component-label is defined, is that defined in px? does the variable subtraction do the right thing on px sizes?

Copy link
Member Author

Choose a reason for hiding this comment

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

app/styles/_variables.less
44:@component-label: 11px;

does the variable subtraction do the right thing on px sizes?

Yes, that works.

}
.section-title {
.h4();
&:not(.no-border) {
Copy link
Member

Choose a reason for hiding this comment

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

do we need to do a negation here, or can we add the border and then set it to none if .no-border is set? not selectors are a little more expensive

<div class="col-sm-7">
<div ng-if="!(overviewBuilds.recentBuildsByBuildConfig[buildConfig.metadata.name] | hashSize)">
No builds.
<!-- <a href="" ng-if="('buildconfigs/instantiate' | canI : 'create')" ng-click="startBuild(buildConfig)" class="small mar-left-sm">Start Build</a> -->
Copy link
Member

Choose a reason for hiding this comment

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

still need this?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, I'll take it out.

@@ -0,0 +1,15 @@
<div class="route-service-bar-chart">
<h5>Traffic Split</h5>
<div ng-repeat="backend in routeServices.backends">
Copy link
Member

Choose a reason for hiding this comment

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

worth doing a trackBy uid here?

Copy link
Member Author

Choose a reason for hiding this comment

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

No UID available, we just have name. We could do a track by backend.name, but I think it's possible for the same name to be there twice (even if it doesn't make sense), which would cause a runtime error.

<a ng-href="{{row.apiObject | editYamlURL}}">Edit YAML</a>
</li>
<li ng-if="(pod = row.firstPod(row.current)) && ('pods/log' | canI : 'get')" role="menuitem">
<a ng-href="{{pod | navigateResourceURL}}?tab=logs">View Logs</a>
Copy link
Member

Choose a reason for hiding this comment

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

This really makes me want to have a logs tab on deployments / statefulsets that just adds a Pod switcher in addition to the container switcher. Follow-on though

Copy link
Member Author

@spadgett spadgett Mar 16, 2017

Choose a reason for hiding this comment

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

This really makes me want to have a logs tab on deployments / statefulsets that just adds a Pod switcher in addition to the container switcher. Follow-on though

+1

<div ng-if="row.apiObject.kind === 'DeploymentConfig'">
<div ng-if="pipeline = row.pipelines[0]">
<p>
This deployment config is part of pipeline
Copy link
Member

Choose a reason for hiding this comment

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

part of the pipeline

pods="row.getPods(row.current)"
containers="row.current.spec.template.spec.containers"
profile="compact"
alerts="row.sate.alerts"
Copy link
Member

Choose a reason for hiding this comment

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

typo
row.state.alerts

pods="row.getPods(row.current)"
containers="row.current.spec.template.spec.containers"
profile="compact"
alerts="row.sate.alerts"
Copy link
Member

Choose a reason for hiding this comment

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

same typo for state

<h3>
<a ng-href="{{service | navigateResourceURL}}">{{service.metadata.name}}</a>
</h3>
<!-- <span class="text-muted mar-right-xs"> -->
Copy link
Member

Choose a reason for hiding this comment

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

can we remove this

<i class="fa fa-external-link small" aria-hidden="true"></i>
</span>
<span ng-if="!(route | isWebRoute)">{{route | routeLabel}}</span>
<route-warnings route="route" service="service"></route-warnings>
Copy link
Member

Choose a reason for hiding this comment

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

wondering if route warnings should be taking into account alternate backends, i know we didnt already just thinking as something for later

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah I had previously opened an issue, #1272

@spadgett
Copy link
Member Author

Added the route beside the app name:

openshift_web_console

Copy link
Member

@jwforres jwforres left a comment

Choose a reason for hiding this comment

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

LGTM, squash please

@jwforres
Copy link
Member

[merge]

@spadgett
Copy link
Member Author

\o/

@openshift-bot
Copy link

Evaluated for origin web console merge up to d7f1197

@openshift-bot
Copy link

openshift-bot commented Mar 16, 2017

Origin Web Console Merge Results: SUCCESS (https://ci.openshift.redhat.com/jenkins/job/test_pull_requests_origin_web_console/1161/) (Base Commit: 136be55)

@openshift-bot openshift-bot merged commit de1b459 into openshift:master Mar 16, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants