Skip to content

Commit

Permalink
feat(Pod): add DSL filter for pods (#2688)
Browse files Browse the repository at this point in the history
* feat(Pod): add DSL filter for pods

Add DSL filters for pods.
They should look similar to the simple service tasks but keep the same functionality as the pods one has now.

Closes DCOS-20249

* fix(PodInstancesView): add more cases for extracting the filter text

Add more cases for extracting the filter text.
Remove unnecessary PodViewFilter.

* fix(PodInstancesContainer): fix DSL region/zone filters

Fix region/zone filters to use the new InstanceUtil.

* test(PodInstances): add DSL filter tests

Add DSL pod instances tests.

* fix(PodInstnancesView): show toggles if the filter type is not text

Show toggles if the filter type is not text.

* refactor(PodInstancesContainer): code improvements for pod DSL filter

Code improvements for pod DSL filter.

* refactor(PodInstancesContainer): code improvements for pod DSL filter

Code improvements for pod DSL filter.

* fix(test-apps): fix system app tests

Fix system app tests.

* refactor(PodInstancesContainer): code improvements for pod DSL filter

Code improvements for pod DSL filter.

* fix(test-apps): add serviceName as a string again

Add serviceName as a string again.

* refactor(PodInstanceTable): make table object agent id more consistent with PodInstance

Make table object agent id more consistent with PodInstance.

* refactor(PodInstanceTable): make table object agent id more consistent with PodInstance

Make table object agent id more consistent with PodInstance.

* refactor(PodInstancesContainer): remove agentId duality

Remove agentId duality.
  • Loading branch information
bstavroulakis authored Feb 22, 2018
1 parent de428e4 commit 57553a6
Show file tree
Hide file tree
Showing 23 changed files with 670 additions and 318 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@ import ServiceInstancesContainer
const HighOrderServiceInstances = function(props) {
const { service, params, location } = props;
if (service instanceof Pod) {
return <PodInstancesContainer pod={service} />;
return (
<PodInstancesContainer
location={location}
params={params}
pod={service}
/>
);
}

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
import { routerShape } from "react-router";
import PropTypes from "prop-types";
import React from "react";

import AppDispatcher from "#SRC/js/events/AppDispatcher";
import ContainerUtil from "#SRC/js/utils/ContainerUtil";
import EventTypes from "#SRC/js/constants/EventTypes";
import MesosStateStore from "#SRC/js/stores/MesosStateStore";
import DSLExpression from "#SRC/js/structs/DSLExpression";
import DSLFilterList from "#SRC/js/structs/DSLFilterList";
import Util from "#SRC/js/utils/Util";

import PodInstanceStatusFilter
from "#PLUGINS/services/src/js/filters/PodInstanceStatusFilter";
import PodInstancesZoneFilter
from "#PLUGINS/services/src/js/filters/PodInstancesZoneFilter";
import PodInstancesRegionFilter
from "#PLUGINS/services/src/js/filters/PodInstancesRegionFilter";
import PodInstanceTextFilter
from "#PLUGINS/services/src/js/filters/PodInstanceTextFilter";

import ActionKeys from "../../constants/ActionKeys";
import MarathonActions from "../../events/MarathonActions";
Expand All @@ -13,6 +26,8 @@ import PodInstancesView from "./PodInstancesView";
import PodUtil from "../../utils/PodUtil";
import ServiceActionItem from "../../constants/ServiceActionItem";
import TaskModals from "../../components/modals/TaskModals";
import InstanceUtil from "../../utils/InstanceUtil";
import TaskMergeDataUtil from "../../utils/TaskMergeDataUtil";

import {
REQUEST_MARATHON_POD_INSTANCE_KILL_ERROR,
Expand All @@ -23,6 +38,7 @@ const METHODS_TO_BIND = [
"handleMesosStateChange",
"handleServerAction",
"handleModalClose",
"handleExpressionChange",
"clearActionError",
"killPodInstances"
];
Expand All @@ -34,7 +50,13 @@ class PodInstancesContainer extends React.Component {
this.state = {
actionErrors: {},
lastUpdate: 0,
pendingActions: {}
pendingActions: {},
filterExpression: new DSLExpression(""),
filters: new DSLFilterList([
new PodInstanceStatusFilter(),
new PodInstanceTextFilter()
]),
defaultFilterData: { zones: [], regions: [] }
};

METHODS_TO_BIND.forEach(method => {
Expand All @@ -52,6 +74,10 @@ class PodInstancesContainer extends React.Component {
this.dispatcher = AppDispatcher.register(this.handleServerAction);
}

componentWillMount() {
this.setFilterOptions(this.props);
}

componentWillUnmount() {
MesosStateStore.removeChangeListener(
EventTypes.MESOS_STATE_CHANGE,
Expand All @@ -61,6 +87,87 @@ class PodInstancesContainer extends React.Component {
AppDispatcher.unregister(this.dispatcher);
}

handleExpressionChange(filterExpression = { value: "" }) {
const { router } = this.context;
const { location: { pathname } } = this.props;
router.push({ pathname, query: { q: filterExpression.value } });

this.setState({ filterExpression });
}

setFilterOptions(props) {
const historicalInstances = MesosStateStore.getPodHistoricalInstances(
props.pod
);

const instances = PodUtil.mergeHistoricalInstanceList(
props.pod.getInstanceList(),
historicalInstances
);

const {
defaultFilterData: { regions, zones },
filterExpression
} = this.state;

const query =
Util.findNestedPropertyInObject(props, "location.query.q") || "is:active";

const mergedInstances = instances
.getItems()
.map(TaskMergeDataUtil.mergeData);

const newZones = Object.keys(
mergedInstances.reduce(function(prev, instance) {
const node = InstanceUtil.getNode(instance);

if (!node || node.getZoneName() === "N/A") {
return prev;
}
prev[node.getZoneName()] = "";

return prev;
}, {})
);

const newRegions = Object.keys(
mergedInstances.reduce(function(prev, instance) {
const node = InstanceUtil.getNode(instance);

if (!node || node.getRegionName() === "N/A") {
return prev;
}
prev[node.getRegionName()] = "";

return prev;
}, {})
);

// If no region/ zones added from props return
if (
newRegions.length === regions.length &&
newRegions.every(region => regions.indexOf(region) !== -1) &&
newZones.length === zones.length &&
newZones.every(zone => zones.indexOf(zone) !== -1) &&
filterExpression.value === query
) {
return;
}

const filters = new DSLFilterList([
new PodInstanceStatusFilter(),
new PodInstancesZoneFilter(newZones),
new PodInstancesRegionFilter(newRegions),
new PodInstanceTextFilter()
]);

this.setState({
filterExpression: new DSLExpression(query),
filters,
defaultFilterData: { regions: newRegions, zones: newZones }
});
}

getChildContext() {
return {
modalHandlers: this.getModalHandlers()
Expand Down Expand Up @@ -203,24 +310,48 @@ class PodInstancesContainer extends React.Component {

render() {
const { pod } = this.props;

const { filterExpression, filters, defaultFilterData } = this.state;
const historicalInstances = MesosStateStore.getPodHistoricalInstances(pod);

let instances = PodUtil.mergeHistoricalInstanceList(
pod.getInstanceList(),
historicalInstances
);

const totalInstances = instances.getItems().length;

instances = instances.mapItems(function(instance) {
instance.agent = MesosStateStore.getNodeFromHostname(
const instanceAgent = MesosStateStore.getNodeFromHostname(
instance.getAgentAddress()
);

if (instanceAgent) {
instance.agentId = instanceAgent.id;
}

return instance;
});

if (filterExpression.defined) {
instances = instances.mapItems(function(instance) {
instance.podSpec = pod.getSpec();

return instance;
});
instances = filterExpression.filter(filters, instances);
}

return (
<div>
<PodInstancesView instances={instances} pod={pod} />
<PodInstancesView
pod={pod}
instances={instances.getItems()}
totalInstances={totalInstances}
handleExpressionChange={this.handleExpressionChange}
filters={filters}
filterExpression={filterExpression}
defaultFilterData={defaultFilterData}
/>
{this.getModals()}
</div>
);
Expand All @@ -235,6 +366,10 @@ PodInstancesContainer.childContextTypes = {
})
};

PodInstancesContainer.contextTypes = {
router: routerShape
};

PodInstancesContainer.propTypes = {
pod: PropTypes.instanceOf(Pod)
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@ import Units from "#SRC/js/utils/Units";
import TableUtil from "#SRC/js/utils/TableUtil";

import Pod from "../../structs/Pod";
import PodInstanceList from "../../structs/PodInstanceList";
import PodInstanceStatus from "../../constants/PodInstanceStatus";
import PodTableHeaderLabels from "../../constants/PodTableHeaderLabels";
import PodUtil from "../../utils/PodUtil";
import InstanceUtil from "../../utils/InstanceUtil";
import PodTableHeaderLabels from "../../constants/PodTableHeaderLabels";

const tableColumnClasses = {
checkbox: "task-table-column-checkbox",
Expand Down Expand Up @@ -65,8 +63,8 @@ class PodInstancesTable extends React.Component {

componentWillReceiveProps(nextProps) {
const { checkedItems } = this.state;
const prevInstances = this.props.instances.getItems();
const nextInstances = nextProps.instances.getItems();
const prevInstances = this.props.instances;
const nextInstances = nextProps.instances;

// When the `instances` property is changed and we have selected
// items, re-trigger selection change in order to remove checked
Expand All @@ -81,7 +79,7 @@ class PodInstancesTable extends React.Component {
}

triggerSelectionChange(checkedItems, instances) {
const checkedItemInstances = instances.getItems().filter(function(item) {
const checkedItemInstances = instances.filter(function(item) {
return checkedItems[item.getId()];
});
this.props.onSelectionChange(checkedItemInstances);
Expand Down Expand Up @@ -273,20 +271,20 @@ class PodInstancesTable extends React.Component {
return children;
}

getDisabledItemsMap(instanceList) {
return instanceList.reduceItems(function(memo, instance) {
getDisabledItemsMap(instances) {
return instances.reduce(function(acc, instance) {
if (!instance.isRunning()) {
memo[instance.getId()] = true;
acc[instance.getId()] = true;
}

return memo;
return acc;
}, {});
}

getTableDataFor(instances, filterText) {
const podSpec = this.props.pod.getSpec();

return instances.getItems().map(instance => {
return instances.map(instance => {
const containers = instance.getContainers().filter(function(container) {
return PodUtil.isContainerMatchingText(container, filterText);
});
Expand All @@ -301,7 +299,7 @@ class PodInstancesTable extends React.Component {
id: instance.getId(),
name: instance.getName(),
address: instance.getAgentAddress(),
agent: instance.agent || { id: instance.agentId },
agentId: instance.agentId,
cpus,
mem,
updated: instance.getLastUpdated(),
Expand All @@ -312,25 +310,6 @@ class PodInstancesTable extends React.Component {
});
}

getInstanceFilterStatus(instance) {
const status = instance.getInstanceStatus();
switch (status) {
case PodInstanceStatus.STAGED:
return "staged";

case PodInstanceStatus.HEALTHY:
case PodInstanceStatus.UNHEALTHY:
case PodInstanceStatus.RUNNING:
return "active";

case PodInstanceStatus.KILLED:
return "completed";

default:
return "";
}
}

renderWithClickHandler(rowOptions, content, className) {
return (
<div onClick={rowOptions.clickHandler} className={className}>
Expand Down Expand Up @@ -388,9 +367,9 @@ class PodInstancesTable extends React.Component {
const { address } = row;

if (rowOptions.isParent) {
const { agent } = row;
const { agentId } = row;

if (!agent) {
if (!agentId) {
return this.renderWithClickHandler(
rowOptions,
<CollapsingString string={address} />
Expand All @@ -401,7 +380,7 @@ class PodInstancesTable extends React.Component {
rowOptions,
<Link
className="table-cell-link-secondary text-overflow"
to={`/nodes/${agent.id}`}
to={`/nodes/${agentId}`}
title={address}
>
<CollapsingString string={address} />
Expand Down Expand Up @@ -510,9 +489,9 @@ class PodInstancesTable extends React.Component {
// If custom list of instances is not provided, use the default instances
// from the pod
if (instances == null) {
instances = pod.getInstanceList();
instances = pod.getInstanceList().getItems();
}
const disabledItems = this.getDisabledItemsMap(instances);
const disabledItemsMap = this.getDisabledItemsMap(instances);

return (
<ExpandingTable
Expand All @@ -523,8 +502,8 @@ class PodInstancesTable extends React.Component {
columns={this.getColumns()}
colGroup={this.getColGroup()}
data={this.getTableDataFor(instances, filterText)}
disabledItemsMap={disabledItems}
inactiveItemsMap={disabledItems}
disabledItemsMap={disabledItemsMap}
inactiveItemsMap={disabledItemsMap}
expandAll={!!filterText}
getColGroup={this.getColGroup}
onCheckboxChange={this.handleItemCheck}
Expand All @@ -546,7 +525,7 @@ PodInstancesTable.defaultProps = {

PodInstancesTable.propTypes = {
filterText: PropTypes.string,
instances: PropTypes.instanceOf(PodInstanceList),
instances: PropTypes.instanceOf(Array),
inverseStyle: PropTypes.bool,
onSelectionChange: PropTypes.func,
pod: PropTypes.instanceOf(Pod).isRequired
Expand Down
Loading

0 comments on commit 57553a6

Please sign in to comment.