You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tez.apache.org by sr...@apache.org on 2016/05/28 11:09:42 UTC
tez git commit: TEZ-3063. Tez UI: Display Input, Output, Processor,
Source and Sink configurations under a vertex (sree)
Repository: tez
Updated Branches:
refs/heads/master de7fd9aa5 -> bcf382ed9
TEZ-3063. Tez UI: Display Input, Output, Processor, Source and Sink configurations under a vertex (sree)
Project: http://git-wip-us.apache.org/repos/asf/tez/repo
Commit: http://git-wip-us.apache.org/repos/asf/tez/commit/bcf382ed
Tree: http://git-wip-us.apache.org/repos/asf/tez/tree/bcf382ed
Diff: http://git-wip-us.apache.org/repos/asf/tez/diff/bcf382ed
Branch: refs/heads/master
Commit: bcf382ed9bbedd25ba4a9cea074d99ef39da5093
Parents: de7fd9a
Author: Sreenath Somarajapuram <sr...@apache.org>
Authored: Sat May 28 16:40:45 2016 +0530
Committer: Sreenath Somarajapuram <sr...@apache.org>
Committed: Sat May 28 16:40:45 2016 +0530
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../src/main/webapp/app/controllers/vertex.js | 3 +
.../webapp/app/controllers/vertex/configs.js | 183 +++++++++++
tez-ui/src/main/webapp/app/models/dag.js | 1 +
tez-ui/src/main/webapp/app/router.js | 1 +
.../src/main/webapp/app/routes/app/configs.js | 2 +-
.../main/webapp/app/routes/vertex/configs.js | 37 +++
tez-ui/src/main/webapp/app/serializers/dag.js | 1 +
tez-ui/src/main/webapp/app/styles/app.less | 1 +
tez-ui/src/main/webapp/app/styles/colors.less | 6 +-
.../main/webapp/app/styles/details-page.less | 7 +
.../webapp/app/styles/vertex-configs-page.less | 101 ++++++
.../webapp/app/templates/vertex/configs.hbs | 189 +++++++++++
tez-ui/src/main/webapp/package.json | 3 +-
.../webapp/tests/unit/controllers/app-test.js | 4 +-
.../tests/unit/controllers/attempt-test.js | 2 +
.../webapp/tests/unit/controllers/dag-test.js | 2 +
.../webapp/tests/unit/controllers/task-test.js | 2 +
.../tests/unit/controllers/vertex-test.js | 2 +
.../unit/controllers/vertex/configs-test.js | 310 +++++++++++++++++++
.../main/webapp/tests/unit/models/dag-test.js | 1 +
.../tests/unit/routes/vertex/configs-test.js | 46 +++
22 files changed, 899 insertions(+), 6 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index ed41b07..f520ee8 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -40,6 +40,7 @@ ALL CHANGES:
TEZ-3255. Tez UI: Hide swimlane while displaying running DAGs from old versions of Tez
TEZ-3259. Tez UI: Build issue - File saver package is not working well with bower
TEZ-3262. Tez UI : zip.js is not having a bower friendly versioning system
+ TEZ-3063. Tez UI: Display Input, Output, Processor, Source and Sink configurations under a vertex
Release 0.8.4: Unreleased
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/controllers/vertex.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/controllers/vertex.js b/tez-ui/src/main/webapp/app/controllers/vertex.js
index 78f5db9..10d992a 100644
--- a/tez-ui/src/main/webapp/app/controllers/vertex.js
+++ b/tez-ui/src/main/webapp/app/controllers/vertex.js
@@ -48,5 +48,8 @@ export default ParentController.extend({
}, {
text: "Task Attempts",
routeName: "vertex.attempts"
+ }, {
+ text: "Configurations",
+ routeName: "vertex.configs"
}]
});
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/controllers/vertex/configs.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/controllers/vertex/configs.js b/tez-ui/src/main/webapp/app/controllers/vertex/configs.js
new file mode 100644
index 0000000..1cf4a3d
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/controllers/vertex/configs.js
@@ -0,0 +1,183 @@
+/*global more*/
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+import TableController from '../table';
+import ColumnDefinition from 'em-table/utils/column-definition';
+
+var MoreObject = more.Object;
+
+// Better fits in more-js
+function arrayfy(object) {
+ var array = [];
+ MoreObject.forEach(object, function (key, value) {
+ array.push({
+ key: key,
+ value: value
+ });
+ });
+ return array;
+}
+
+export default TableController.extend({
+ searchText: "tez",
+
+ queryParams: ["configType", "configID"],
+ configType: null,
+ configID: null,
+
+ breadcrumbs: Ember.computed("configDetails", "configID", "configType", function () {
+ var crumbs = [{
+ text: "Configurations",
+ routeName: "vertex.configs",
+ queryParams: {
+ configType: null,
+ configID: null,
+ }
+ }],
+ type = this.get("configType"),
+ name;
+
+ if(this.get("configType")) {
+ name = this.get("configDetails.name") || this.get("configDetails.desc");
+ }
+
+ if(type && name) {
+ type = type.capitalize();
+ crumbs.push({
+ text: `${type} [ ${name} ]`,
+ routeName: "vertex.configs",
+ });
+ }
+
+ return crumbs;
+ }),
+
+ setBreadcrumbs: function() {
+ this._super();
+ Ember.run.later(this, "send", "bubbleBreadcrumbs", []);
+ },
+
+ columns: ColumnDefinition.make([{
+ id: 'configName',
+ headerTitle: 'Configuration Name',
+ contentPath: 'configName',
+ }, {
+ id: 'configValue',
+ headerTitle: 'Configuration Value',
+ contentPath: 'configValue',
+ }]),
+
+ normalizeConfig: function (config) {
+ var userPayload = config.userPayloadAsText ? JSON.parse(config.userPayloadAsText) : {};
+ return {
+ id: config.name || null,
+ name: config.name,
+ desc: userPayload.desc,
+ class: config.class || config.processorClass,
+ initializer: config.initializer,
+ configs: arrayfy(userPayload.config || {})
+ };
+ },
+
+ configsHash: Ember.computed("model.name", "model.dag.vertices", function () {
+ var vertexName = this.get("model.name"),
+
+ inputConfigs = [],
+ outputConfigs = [],
+ vertexDetails;
+
+ if(!this.get("model")) {
+ return {};
+ }
+
+ vertexDetails = this.get("model.dag.vertices").findBy("vertexName", vertexName);
+
+ (this.get("model.dag.edges") || []).forEach(function (edge) {
+ if(edge.outputVertexName === vertexName) {
+ let payload = edge.outputUserPayloadAsText;
+ inputConfigs.push({
+ id: edge.edgeId,
+ desc: `From ${edge.inputVertexName}`,
+ class: edge.edgeDestinationClass,
+ configs: arrayfy(payload ? Ember.get(JSON.parse(payload), "config") : {})
+ });
+ }
+ else if(edge.inputVertexName === vertexName) {
+ let payload = edge.inputUserPayloadAsText;
+ outputConfigs.push({
+ id: edge.edgeId,
+ desc: `To ${edge.outputVertexName}`,
+ class: edge.edgeSourceClass,
+ configs: arrayfy(payload ? Ember.get(JSON.parse(payload), "config") : {})
+ });
+ }
+ });
+
+ return {
+ processor: this.normalizeConfig(vertexDetails),
+
+ sources: (vertexDetails.additionalInputs || []).map(this.normalizeConfig),
+ sinks: (vertexDetails.additionalOutputs || []).map(this.normalizeConfig),
+
+ inputs: inputConfigs,
+ outputs: outputConfigs
+ };
+ }),
+
+ configDetails: Ember.computed("configsHash", "configType", "configID", function () {
+ var configType = this.get("configType"),
+ details;
+
+ if(configType) {
+ details = Ember.get(this.get("configsHash"), configType);
+ }
+
+ if(Array.isArray(details)) {
+ details = details.findBy("id", this.get("configID"));
+ }
+
+ return details;
+ }),
+
+ configs: Ember.computed("configDetails", function () {
+ var configs = this.get("configDetails.configs");
+
+ if(Array.isArray(configs)) {
+ return Ember.A(configs.map(function (config) {
+ return Ember.Object.create({
+ configName: config.key,
+ configValue: config.value
+ });
+ }));
+ }
+ }),
+
+ actions: {
+ showConf: function (type, details) {
+ this.setProperties({
+ configType: type,
+ configID: details.id
+ });
+ Ember.run.later(this, "send", "bubbleBreadcrumbs", []);
+ }
+ }
+
+});
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/models/dag.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/models/dag.js b/tez-ui/src/main/webapp/app/models/dag.js
index 84509e1..5e011e2 100644
--- a/tez-ui/src/main/webapp/app/models/dag.js
+++ b/tez-ui/src/main/webapp/app/models/dag.js
@@ -63,6 +63,7 @@ export default AMTimelineModel.extend({
}),
vertexIdNameMap: DS.attr("object"),
+ vertexNameIdMap: DS.attr("object"),
callerID: DS.attr("string"),
callerContext: DS.attr("string"),
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/router.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/router.js b/tez-ui/src/main/webapp/app/router.js
index 98a456a..7f18ae6 100644
--- a/tez-ui/src/main/webapp/app/router.js
+++ b/tez-ui/src/main/webapp/app/router.js
@@ -38,6 +38,7 @@ Router.map(function() {
this.route('tasks');
this.route('attempts');
this.route('counters');
+ this.route('configs');
});
this.route('task', {path: '/task/:task_id'}, function() {
this.route('attempts');
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/routes/app/configs.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/routes/app/configs.js b/tez-ui/src/main/webapp/app/routes/app/configs.js
index bdd53ae..c58f2f1 100644
--- a/tez-ui/src/main/webapp/app/routes/app/configs.js
+++ b/tez-ui/src/main/webapp/app/routes/app/configs.js
@@ -20,7 +20,7 @@ import Ember from 'ember';
import SingleAmPollsterRoute from '../single-am-pollster';
export default SingleAmPollsterRoute.extend({
- title: "Application Details",
+ title: "Application Configurations",
loaderNamespace: "app",
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/routes/vertex/configs.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/routes/vertex/configs.js b/tez-ui/src/main/webapp/app/routes/vertex/configs.js
new file mode 100644
index 0000000..a131896
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/routes/vertex/configs.js
@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+import SingleAmPollsterRoute from '../single-am-pollster';
+
+export default SingleAmPollsterRoute.extend({
+ title: "Vertex Configurations",
+
+ loaderNamespace: "vertex",
+
+ canPoll: false,
+
+ setupController: function (controller, model) {
+ this._super(controller, model);
+ Ember.run.later(this, "startCrumbBubble");
+ },
+
+ load: function (value, query, options) {
+ return this.get("loader").queryRecord('vertex', this.modelFor("vertex").get("id"), options);
+ },
+});
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/serializers/dag.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/serializers/dag.js b/tez-ui/src/main/webapp/app/serializers/dag.js
index a64bfe1..bf31174 100644
--- a/tez-ui/src/main/webapp/app/serializers/dag.js
+++ b/tez-ui/src/main/webapp/app/serializers/dag.js
@@ -125,6 +125,7 @@ export default TimelineSerializer.extend({
containerLogs: getContainerLogs,
vertexIdNameMap: getIdNameMap,
+ vertexNameIdMap: 'otherinfo.vertexNameIdMapping',
callerID: 'primaryfilters.callerId.0',
callerContext: 'callerContext',
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/styles/app.less
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/styles/app.less b/tez-ui/src/main/webapp/app/styles/app.less
index c881553..b4a4c47 100644
--- a/tez-ui/src/main/webapp/app/styles/app.less
+++ b/tez-ui/src/main/webapp/app/styles/app.less
@@ -50,3 +50,4 @@
@import "page-layout";
@import "details-page";
@import "swimlane-page";
+@import "vertex-configs-page";
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/styles/colors.less
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/styles/colors.less b/tez-ui/src/main/webapp/app/styles/colors.less
index af470ff..bc4a398 100644
--- a/tez-ui/src/main/webapp/app/styles/colors.less
+++ b/tez-ui/src/main/webapp/app/styles/colors.less
@@ -19,14 +19,14 @@
// Colors
@logo-orange: #D27A22;
-@bg-lite: #f5f5f5;
+@bg-lite: #f0f0f0;
@bg-liter: #f5f5f5;
@bg-red-light: #FFE6E6;
@bg-grey: #f0f0f0;
@border-lite: #e5e5e5;
-@border-color: #dcdcdc;
+@border-color: #ddd;
@white: #fff;
@@ -41,4 +41,4 @@
@success-color: limegreen;
@error-color: crimson;
@warning-color: orange;
-@unknown-color: crimson;
\ No newline at end of file
+@unknown-color: crimson;
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/styles/details-page.less
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/styles/details-page.less b/tez-ui/src/main/webapp/app/styles/details-page.less
index 75d5e11..54380e2 100644
--- a/tez-ui/src/main/webapp/app/styles/details-page.less
+++ b/tez-ui/src/main/webapp/app/styles/details-page.less
@@ -22,6 +22,10 @@
margin: 0 10px 10px 0;
+ &:first-child {
+ margin: 0 0 10px 0;
+ }
+
table-layout: fixed;
.progress {
@@ -40,7 +44,10 @@
td {
padding: 0px 20px 0px 0px;
+
+ max-width: 600px;
white-space: nowrap;
+ overflow: auto;
.ember-view {
display: inline;
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/styles/vertex-configs-page.less
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/styles/vertex-configs-page.less b/tez-ui/src/main/webapp/app/styles/vertex-configs-page.less
new file mode 100644
index 0000000..ffa1bdd
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/styles/vertex-configs-page.less
@@ -0,0 +1,101 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.vertex-configs-container {
+ display: inline-block;
+ width: 40%;
+ vertical-align: top;
+
+ .row-container {
+ display: flex;
+ }
+
+ .column-container {
+ display: inline-block;
+ }
+
+ .column-container, .processor {
+ margin: 0 5px 0 5px;
+ flex: 1 1;
+
+ .box {
+ border: 1px solid @border-color;
+ border-radius: 5px;
+ overflow: hidden;
+
+ .header, .config-cell {
+ padding: 5px;
+ }
+
+ .header {
+ height: auto;
+ margin: 0px;
+ }
+
+ .config-cell {
+ cursor: pointer;
+
+ border-top: 1px dotted @border-color;
+
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+
+ .count-txt {
+ color: @text-light;
+ font-size: .8em;
+ }
+
+ &:nth-child(2) {
+ border-top: none;
+ }
+
+ &.selected {
+ background-color: @bg-liter;
+ }
+
+ &:hover {
+ background-color: @bg-lite;
+ }
+ }
+ }
+
+ .link {
+ border-left: 1px solid @border-color;
+ margin-left: 50%;
+ height: 10px;
+ }
+ }
+
+ .top-column {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-end;
+ }
+
+ .processor {
+ text-align: center;
+ }
+
+}
+
+.configuration-details {
+ display: inline-block;
+ width: 60%;
+ padding-left: 10px;
+}
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/app/templates/vertex/configs.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/templates/vertex/configs.hbs b/tez-ui/src/main/webapp/app/templates/vertex/configs.hbs
new file mode 100644
index 0000000..de6d3a9
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/templates/vertex/configs.hbs
@@ -0,0 +1,189 @@
+{{!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+}}
+
+{{#if loaded}}
+
+ <!-- Vertex configuration visualization -->
+ <div class="vertex-configs-container">
+ <div class="row-container">
+ {{#if configsHash.sources.length}}
+ <div class="column-container top-column">
+ <div class="box">
+ <div class="header">Sources</div>
+ {{#each configsHash.sources as |source|}}
+ <div class="config-cell {{if (eq source configDetails) 'selected'}}"
+ {{action 'showConf' 'sources' source}}>
+ {{source.name}}
+ {{#if source.desc}}
+ [ {{source.desc}} ]
+ {{/if}}
+ <div class="count-txt">
+ {{#if source.configs.length}}
+ Configurations: {{source.configs.length}}
+ {{else}}
+ Configuration not available!
+ {{/if}}
+ </div>
+ </div>
+ {{/each}}
+ </div>
+ <div class="link"></div>
+ </div>
+ {{/if}}
+ {{#if configsHash.inputs.length}}
+ <div class="column-container top-column">
+ <div class="box">
+ <div class="header">Inputs</div>
+ {{#each configsHash.inputs as |input|}}
+ <div class="config-cell {{if (eq input configDetails) 'selected'}}"
+ {{action 'showConf' 'inputs' input}}>
+ {{input.desc}}
+ <div class="count-txt">
+ {{#if input.configs.length}}
+ Configurations: {{input.configs.length}}
+ {{else}}
+ Configuration not available!
+ {{/if}}
+ </div>
+ </div>
+ {{/each}}
+ </div>
+ <div class="link"></div>
+ </div>
+ {{/if}}
+ </div>
+ <div class="processor">
+ <div class="box">
+ <div class="header">Processor</div>
+ <div class="config-cell {{if (eq configsHash.processor configDetails) 'selected'}}"
+ {{action 'showConf' 'processor' configsHash.processor}}>
+ {{configsHash.processor.desc}}
+ <div class="count-txt">
+ {{#if configsHash.processor.configs.length}}
+ Configurations: {{configsHash.processor.configs.length}}
+ {{else}}
+ Configuration not available!
+ {{/if}}
+ </div>
+ </div>
+ </div>
+ </div>
+ <div class="row-container">
+ {{#if configsHash.sinks.length}}
+ <div class="column-container">
+ <div class="link"></div>
+ <div class="box">
+ <div class="header">Sinks</div>
+ {{#each configsHash.sinks as |sink|}}
+ <div class="config-cell {{if (eq sink configDetails) 'selected'}}"
+ {{action 'showConf' 'sinks' sink}}>
+ {{sink.name}}
+ {{#if sink.desc}}
+ [ {{sink.desc}} ]
+ {{/if}}
+ <div class="count-txt">
+ {{#if sink.configs.length}}
+ Configurations: {{sink.configs.length}}
+ {{else}}
+ Configuration not available!
+ {{/if}}
+ </div>
+ </div>
+ {{/each}}
+ </div>
+ </div>
+ {{/if}}
+ {{#if configsHash.outputs.length}}
+ <div class="column-container">
+ <div class="link"></div>
+ <div class="box">
+ <div class="header">Outputs</div>
+ {{#each configsHash.outputs as |output|}}
+ <div class="config-cell {{if (eq output configDetails) 'selected'}}"
+ {{action 'showConf' 'outputs' output}}>
+ {{output.desc}}
+ <div class="count-txt">
+ {{#if output.configs.length}}
+ Configurations: {{output.configs.length}}
+ {{else}}
+ Configuration not available!
+ {{/if}}
+ </div>
+ </div>
+ {{/each}}
+ </div>
+ </div>
+ {{/if}}
+ </div>
+ </div><div class="configuration-details">
+
+ {{#if configType}}
+ <!-- Configuration details display -->
+ <table class='detail-list'>
+ <thead>
+ <tr>
+ <th colspan=2>Details</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{#if configDetails.name}}
+ <tr>
+ <td>Name</td>
+ <td>{{configDetails.name}}</td>
+ </tr>
+ {{/if}}
+ {{#if configDetails.desc}}
+ <tr>
+ <td>Description</td>
+ <td>{{configDetails.desc}}</td>
+ </tr>
+ {{/if}}
+ {{#if configDetails.class}}
+ <tr>
+ <td>Class</td>
+ <td>{{configDetails.class}}</td>
+ </tr>
+ {{/if}}
+ {{#if configDetails.initializer}}
+ <tr>
+ <td>Initializer</td>
+ <td>{{configDetails.initializer}}</td>
+ </tr>
+ {{/if}}
+ </tbody>
+ </table>
+
+ <!-- Configurations display -->
+ {{em-table
+ columns=columns
+ rows=configs
+
+ rowCount=configs.length
+ definition=definition
+
+ enablePagination=false
+
+ searchAction="searchChanged"
+ sortAction="sortChanged"
+ }}
+ {{/if}}
+ </div>
+
+{{else}}
+ {{partial "loading"}}
+{{/if}}
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/package.json
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/package.json b/tez-ui/src/main/webapp/package.json
index accddec..7916d85 100644
--- a/tez-ui/src/main/webapp/package.json
+++ b/tez-ui/src/main/webapp/package.json
@@ -53,10 +53,11 @@
"ember-disable-proxy-controllers": "1.0.1",
"ember-export-application-global": "1.0.5",
"ember-resolver": "2.0.3",
+ "ember-truth-helpers": "1.2.0",
"phantomjs": "1.9.19"
},
"dependencies": {
- "em-helpers": "0.5.8",
+ "em-helpers": "0.5.9",
"em-table": "0.3.12",
"em-tgraph": "0.0.5"
}
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/tests/unit/controllers/app-test.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/tests/unit/controllers/app-test.js b/tez-ui/src/main/webapp/tests/unit/controllers/app-test.js
index 2fc7276..f969d7e 100644
--- a/tez-ui/src/main/webapp/tests/unit/controllers/app-test.js
+++ b/tez-ui/src/main/webapp/tests/unit/controllers/app-test.js
@@ -20,7 +20,7 @@ import Ember from 'ember';
import { moduleFor, test } from 'ember-qunit';
-moduleFor('controller:dag', 'Unit | Controller | dag', {
+moduleFor('controller:app', 'Unit | Controller | app', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
@@ -34,4 +34,6 @@ test('Basic creation test', function(assert) {
assert.ok(controller);
assert.ok(controller.breadcrumbs);
assert.ok(controller.tabs);
+
+ assert.equal(controller.tabs.length, 3);
});
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/tests/unit/controllers/attempt-test.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/tests/unit/controllers/attempt-test.js b/tez-ui/src/main/webapp/tests/unit/controllers/attempt-test.js
index da451f7..4053470 100644
--- a/tez-ui/src/main/webapp/tests/unit/controllers/attempt-test.js
+++ b/tez-ui/src/main/webapp/tests/unit/controllers/attempt-test.js
@@ -34,4 +34,6 @@ test('Basic creation test', function(assert) {
assert.ok(controller);
assert.ok(controller.breadcrumbs);
assert.ok(controller.tabs);
+
+ assert.equal(controller.tabs.length, 2);
});
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/tests/unit/controllers/dag-test.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/tests/unit/controllers/dag-test.js b/tez-ui/src/main/webapp/tests/unit/controllers/dag-test.js
index 2fc7276..f40cd48 100644
--- a/tez-ui/src/main/webapp/tests/unit/controllers/dag-test.js
+++ b/tez-ui/src/main/webapp/tests/unit/controllers/dag-test.js
@@ -34,4 +34,6 @@ test('Basic creation test', function(assert) {
assert.ok(controller);
assert.ok(controller.breadcrumbs);
assert.ok(controller.tabs);
+
+ assert.equal(controller.tabs.length, 7);
});
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/tests/unit/controllers/task-test.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/tests/unit/controllers/task-test.js b/tez-ui/src/main/webapp/tests/unit/controllers/task-test.js
index c9cee79..98c338b 100644
--- a/tez-ui/src/main/webapp/tests/unit/controllers/task-test.js
+++ b/tez-ui/src/main/webapp/tests/unit/controllers/task-test.js
@@ -34,4 +34,6 @@ test('Basic creation test', function(assert) {
assert.ok(controller);
assert.ok(controller.breadcrumbs);
assert.ok(controller.tabs);
+
+ assert.equal(controller.tabs.length, 3);
});
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/tests/unit/controllers/vertex-test.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/tests/unit/controllers/vertex-test.js b/tez-ui/src/main/webapp/tests/unit/controllers/vertex-test.js
index e8e9b3f..2283110 100644
--- a/tez-ui/src/main/webapp/tests/unit/controllers/vertex-test.js
+++ b/tez-ui/src/main/webapp/tests/unit/controllers/vertex-test.js
@@ -34,4 +34,6 @@ test('Basic creation test', function(assert) {
assert.ok(controller);
assert.ok(controller.breadcrumbs);
assert.ok(controller.tabs);
+
+ assert.equal(controller.tabs.length, 5);
});
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/tests/unit/controllers/vertex/configs-test.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/tests/unit/controllers/vertex/configs-test.js b/tez-ui/src/main/webapp/tests/unit/controllers/vertex/configs-test.js
new file mode 100644
index 0000000..89015a5
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/unit/controllers/vertex/configs-test.js
@@ -0,0 +1,310 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Ember from 'ember';
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('controller:vertex/configs', 'Unit | Controller | vertex/configs', {
+ // Specify the other units that are required for this test.
+ // needs: ['controller:foo']
+});
+
+test('Basic creation test', function(assert) {
+ let controller = this.subject({
+ send: Ember.K,
+ initVisibleColumns: Ember.K
+ });
+
+ assert.ok(controller);
+
+ assert.ok(controller.breadcrumbs);
+ assert.ok(controller.setBreadcrumbs);
+
+ assert.ok(controller.columns);
+ assert.equal(controller.columns.length, 2);
+
+ assert.ok(controller.normalizeConfig);
+ assert.ok(controller.configsHash);
+ assert.ok(controller.configDetails);
+ assert.ok(controller.configs);
+
+ assert.ok(controller.actions.showConf);
+
+ assert.equal(controller.searchText, "tez");
+ assert.notEqual(controller.queryParams.indexOf("configType"), -1);
+ assert.notEqual(controller.queryParams.indexOf("configID"), -1);
+});
+
+test('Breadcrumbs test', function(assert) {
+ let controller = this.subject({
+ send: Ember.K,
+ initVisibleColumns: Ember.K,
+ configDetails: {
+ name: "name"
+ }
+ });
+
+ assert.equal(controller.get("breadcrumbs").length, 1);
+ assert.equal(controller.get("breadcrumbs")[0].text, "Configurations");
+ assert.equal(controller.get("breadcrumbs")[0].queryParams.configType, null);
+ assert.equal(controller.get("breadcrumbs")[0].queryParams.configID, null);
+
+ controller.setProperties({
+ configType: "TestType",
+ configID: "ID",
+ });
+ assert.equal(controller.get("breadcrumbs").length, 2);
+ assert.equal(controller.get("breadcrumbs")[1].text, "TestType [ name ]");
+});
+
+test('normalizeConfig test', function(assert) {
+ let controller = this.subject({
+ send: Ember.K,
+ initVisibleColumns: Ember.K,
+ }),
+ testName = "name",
+ testClass = "TestClass",
+ testInit = "TestInit",
+ payload = {
+ desc: 'abc',
+ config: {
+ x:1,
+ y:2
+ }
+ },
+ config;
+
+ // Processor
+ config = controller.normalizeConfig({
+ processorClass: testClass,
+ userPayloadAsText: JSON.stringify(payload)
+ });
+ assert.equal(config.id, null);
+ assert.equal(config.class, testClass);
+ assert.equal(config.desc, payload.desc);
+ assert.deepEqual(config.configs, [{key: "x", value: 1}, {key: "y", value: 2}]);
+
+ // Inputs & outputs
+ config = controller.normalizeConfig({
+ name: testName,
+ class: testClass,
+ initializer: testInit,
+ userPayloadAsText: JSON.stringify(payload)
+ });
+ assert.equal(config.id, testName);
+ assert.equal(config.class, testClass);
+ assert.equal(config.initializer, testInit);
+ assert.equal(config.desc, payload.desc);
+ assert.deepEqual(config.configs, [{key: "x", value: 1}, {key: "y", value: 2}]);
+});
+
+test('configsHash test', function(assert) {
+ let controller = this.subject({
+ send: Ember.K,
+ initVisibleColumns: Ember.K,
+ });
+
+ assert.deepEqual(controller.get("configsHash"), {});
+
+ controller.set("model", {
+ dag: {
+ vertices: [
+ {
+ "vertexName": "v1",
+ "processorClass": "org.apache.tez.mapreduce.processor.map.MapProcessor",
+ "userPayloadAsText": "{\"desc\":\"Tokenizer Vertex\",\"config\":{\"config.key\":\"11\"}}",
+ "additionalInputs": [
+ {
+ "name": "MRInput",
+ "class": "org.apache.tez.mapreduce.input.MRInputLegacy",
+ "initializer": "org.apache.tez.mapreduce.common.MRInputAMSplitGenerator",
+ "userPayloadAsText": "{\"desc\":\"HDFS Input\",\"config\":{\"config.key\":\"22\"}}"
+ }
+ ]
+ },
+ {
+ "vertexName": "v2",
+ "processorClass": "org.apache.tez.mapreduce.processor.reduce.ReduceProcessor",
+ "userPayloadAsText": "{\"desc\":\"Summation Vertex\",\"config\":{\"config.key\":\"33\"}}"
+ },
+ {
+ "vertexName": "v3",
+ "processorClass": "org.apache.tez.mapreduce.processor.reduce.ReduceProcessor",
+ "userPayloadAsText": "{\"desc\":\"Sorter Vertex\",\"config\":{\"config.key1\":\"44\", \"config.key2\":\"444\"}}",
+ "additionalOutputs": [
+ {
+ "name": "MROutput",
+ "class": "org.apache.tez.mapreduce.output.MROutputLegacy",
+ "initializer": "org.apache.tez.mapreduce.committer.MROutputCommitter",
+ "userPayloadAsText": "{\"desc\":\"HDFS Output\",\"config\":{\"config.key\":\"55\"}}"
+ }
+ ]
+ }
+ ],
+ edges: [
+ {
+ "edgeId": "edg1",
+ "inputVertexName": "v2",
+ "outputVertexName": "v3",
+ "edgeSourceClass": "org.apache.tez.runtime.library.output.OrderedPartitionedKVOutput",
+ "edgeDestinationClass": "org.apache.tez.runtime.library.input.OrderedGroupedInputLegacy",
+ "outputUserPayloadAsText": "{\"config\":{\"config.key\":\"66\"}}",
+ "inputUserPayloadAsText": "{\"config\":{\"config.key\":\"77\"}}",
+ },
+ {
+ "edgeId": "edg2",
+ "inputVertexName": "v1",
+ "outputVertexName": "v2",
+ "edgeSourceClass": "org.apache.tez.runtime.library.output.OrderedPartitionedKVOutput",
+ "edgeDestinationClass": "org.apache.tez.runtime.library.input.OrderedGroupedInputLegacy",
+ "outputUserPayloadAsText": "{\"config\":{\"config.key\":\"88\"}}",
+ "inputUserPayloadAsText": "{\"config\":{\"config.key\":\"99\"}}",
+ }
+ ]
+ }
+ });
+
+ // Test for vertex v1
+ controller.set("model.name", "v1");
+
+ assert.ok(controller.get("configsHash.processor"));
+ assert.equal(controller.get("configsHash.processor.name"), null);
+ assert.equal(controller.get("configsHash.processor.desc"), "Tokenizer Vertex");
+ assert.equal(controller.get("configsHash.processor.class"), "org.apache.tez.mapreduce.processor.map.MapProcessor");
+ assert.equal(controller.get("configsHash.processor.configs.length"), 1);
+ assert.equal(controller.get("configsHash.processor.configs.0.key"), "config.key");
+ assert.equal(controller.get("configsHash.processor.configs.0.value"), 11);
+
+ assert.ok(controller.get("configsHash.sources"));
+ assert.equal(controller.get("configsHash.sources.length"), 1);
+ assert.equal(controller.get("configsHash.sources.0.name"), "MRInput");
+ assert.equal(controller.get("configsHash.sources.0.desc"), "HDFS Input");
+ assert.equal(controller.get("configsHash.sources.0.class"), "org.apache.tez.mapreduce.input.MRInputLegacy");
+ assert.equal(controller.get("configsHash.sources.0.initializer"), "org.apache.tez.mapreduce.common.MRInputAMSplitGenerator");
+ assert.equal(controller.get("configsHash.sources.0.configs.length"), 1);
+ assert.equal(controller.get("configsHash.sources.0.configs.0.key"), "config.key");
+ assert.equal(controller.get("configsHash.sources.0.configs.0.value"), 22);
+
+ assert.ok(controller.get("configsHash.sinks"));
+ assert.equal(controller.get("configsHash.sinks.length"), 0);
+
+ assert.ok(controller.get("configsHash.inputs"));
+ assert.equal(controller.get("configsHash.inputs.length"), 0);
+
+ assert.ok(controller.get("configsHash.outputs"));
+ assert.equal(controller.get("configsHash.outputs.length"), 1);
+ assert.equal(controller.get("configsHash.outputs.0.name"), null);
+ assert.equal(controller.get("configsHash.outputs.0.desc"), "To v2");
+ assert.equal(controller.get("configsHash.outputs.0.class"), "org.apache.tez.runtime.library.output.OrderedPartitionedKVOutput");
+ assert.equal(controller.get("configsHash.outputs.0.configs.length"), 1);
+ assert.equal(controller.get("configsHash.outputs.0.configs.0.key"), "config.key");
+ assert.equal(controller.get("configsHash.outputs.0.configs.0.value"), 99);
+
+ // Test for vertex v3
+ controller.set("model.name", "v3");
+
+ assert.ok(controller.get("configsHash.processor"));
+ assert.equal(controller.get("configsHash.processor.name"), null);
+ assert.equal(controller.get("configsHash.processor.desc"), "Sorter Vertex");
+ assert.equal(controller.get("configsHash.processor.class"), "org.apache.tez.mapreduce.processor.reduce.ReduceProcessor");
+ assert.equal(controller.get("configsHash.processor.configs.length"), 2);
+ assert.equal(controller.get("configsHash.processor.configs.0.key"), "config.key1");
+ assert.equal(controller.get("configsHash.processor.configs.0.value"), 44);
+ assert.equal(controller.get("configsHash.processor.configs.1.key"), "config.key2");
+ assert.equal(controller.get("configsHash.processor.configs.1.value"), 444);
+
+ assert.ok(controller.get("configsHash.sources"));
+ assert.equal(controller.get("configsHash.sources.length"), 0);
+
+ assert.ok(controller.get("configsHash.sinks"));
+ assert.equal(controller.get("configsHash.sinks.length"), 1);
+ assert.equal(controller.get("configsHash.sinks.0.name"), "MROutput");
+ assert.equal(controller.get("configsHash.sinks.0.desc"), "HDFS Output");
+ assert.equal(controller.get("configsHash.sinks.0.class"), "org.apache.tez.mapreduce.output.MROutputLegacy");
+ assert.equal(controller.get("configsHash.sinks.0.initializer"), "org.apache.tez.mapreduce.committer.MROutputCommitter");
+ assert.equal(controller.get("configsHash.sinks.0.configs.length"), 1);
+ assert.equal(controller.get("configsHash.sinks.0.configs.0.key"), "config.key");
+ assert.equal(controller.get("configsHash.sinks.0.configs.0.value"), 55);
+
+ assert.ok(controller.get("configsHash.inputs"));
+ assert.equal(controller.get("configsHash.inputs.length"), 1);
+ assert.equal(controller.get("configsHash.inputs.0.name"), null);
+ assert.equal(controller.get("configsHash.inputs.0.desc"), "From v2");
+ assert.equal(controller.get("configsHash.inputs.0.class"), "org.apache.tez.runtime.library.input.OrderedGroupedInputLegacy");
+ assert.equal(controller.get("configsHash.inputs.0.configs.length"), 1);
+ assert.equal(controller.get("configsHash.inputs.0.configs.0.key"), "config.key");
+ assert.equal(controller.get("configsHash.inputs.0.configs.0.value"), 66);
+
+ assert.ok(controller.get("configsHash.outputs"));
+ assert.equal(controller.get("configsHash.outputs.length"), 0);
+
+});
+
+test('configDetails test', function(assert) {
+ let configsHash = {
+ type: [{
+ id: "id1"
+ },{
+ id: "id2"
+ }]
+ },
+ controller = this.subject({
+ send: Ember.K,
+ initVisibleColumns: Ember.K,
+ configsHash: configsHash
+ });
+
+ assert.equal(controller.get("configDetails"), undefined);
+
+ controller.set("configType", "random");
+ assert.equal(controller.get("configDetails"), undefined);
+
+ controller.set("configType", "type");
+ assert.equal(controller.get("configDetails"), undefined);
+
+ controller.set("configID", "id1");
+ assert.equal(controller.get("configDetails"), configsHash.type[0]);
+
+ controller.set("configID", "id2");
+ assert.equal(controller.get("configDetails"), configsHash.type[1]);
+});
+
+test('configs test', function(assert) {
+ let controller = this.subject({
+ send: Ember.K,
+ initVisibleColumns: Ember.K,
+ configDetails: {
+ configs: [{
+ key: "x",
+ value: 1
+ }, {
+ key: "y",
+ value: 2
+ }]
+ }
+ });
+
+ assert.equal(controller.get("configs").length, 2);
+ assert.ok(controller.get("configs.0") instanceof Ember.Object);
+
+ assert.equal(controller.get("configs.0.configName"), "x");
+ assert.equal(controller.get("configs.0.configValue"), 1);
+ assert.equal(controller.get("configs.1.configName"), "y");
+ assert.equal(controller.get("configs.1.configValue"), 2);
+});
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/tests/unit/models/dag-test.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/tests/unit/models/dag-test.js b/tez-ui/src/main/webapp/tests/unit/models/dag-test.js
index 78affcf..2c07f7b 100644
--- a/tez-ui/src/main/webapp/tests/unit/models/dag-test.js
+++ b/tez-ui/src/main/webapp/tests/unit/models/dag-test.js
@@ -49,6 +49,7 @@ test('Basic creation test', function(assert) {
assert.ok(model.containerLogs);
assert.ok(model.vertexIdNameMap);
+ assert.ok(model.vertexNameIdMap);
assert.ok(model.callerID);
assert.ok(model.callerContext);
http://git-wip-us.apache.org/repos/asf/tez/blob/bcf382ed/tez-ui/src/main/webapp/tests/unit/routes/vertex/configs-test.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/tests/unit/routes/vertex/configs-test.js b/tez-ui/src/main/webapp/tests/unit/routes/vertex/configs-test.js
new file mode 100644
index 0000000..e293a7e
--- /dev/null
+++ b/tez-ui/src/main/webapp/tests/unit/routes/vertex/configs-test.js
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { moduleFor, test } from 'ember-qunit';
+
+moduleFor('route:vertex/configs', 'Unit | Route | vertex/configs', {
+ // Specify the other units that are required for this test.
+ // needs: ['controller:foo']
+});
+
+test('Basic creation test', function(assert) {
+ let route = this.subject();
+
+ assert.ok(route);
+ assert.ok(route.title);
+ assert.ok(route.loaderNamespace);
+ assert.ok(route.setupController);
+ assert.ok(route.load);
+});
+
+test('setupController test', function(assert) {
+ assert.expect(1);
+
+ let route = this.subject({
+ startCrumbBubble: function () {
+ assert.ok(true);
+ }
+ });
+
+ route.setupController({}, {});
+});